C# 反射(Reflection)

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。

您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优缺点

优点:

  • 1、反射提高了程序的灵活性和扩展性。

  • 2、降低耦合性,提高自适应能力。

  • 3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

  • 1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。

  • 2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

反射(Reflection)的用途

反射(Reflection)有下列用途:

  • 它允许在运行时查看特性(attribute)信息。

  • 它允许审查集合中的各种类型,以及实例化这些类型。

  • 它允许延迟绑定的方法和属性(property)。

  • 它允许在运行时创建新类型,然后使用这些类型执行一些任务。

查看元数据

我们已经在上面的章节中提到过,使用反射(Reflection)可以查看特性(attribute)信息。

System.Reflection 类的 MemberInfo 对象需要被初始化,用于发现与类相关的特性(attribute)。为了做到这点,您可以定义目标类的一个对象,如下:

1
System.Reflection.MemberInfo info = typeof(MyClass);

下面的程序演示了这点:

> 实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using System;

[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute : System.Attribute
{
public readonly string Url;

public string Topic // Topic 是一个命名(named)参数
{
get
{
return topic;
}
set
{

topic = value;
}
}

public HelpAttribute(string url) // url 是一个定位(positional)参数
{
this.Url = url;
}

private string topic;
}
[HelpAttribute("Information on the class MyClass")]
class MyClass
{
}

namespace AttributeAppl
{
class Program
{
static void Main(string[] args)
{
System.Reflection.MemberInfo info = typeof(MyClass);
object[] attributes = info.GetCustomAttributes(true);
for (int i = 0; i < attributes.Length; i++)
{
System.Console.WriteLine(attributes[i]);
}
Console.ReadKey();

}
}
}

当上面的代码被编译和执行时,它会显示附加到类 MyClass 上的自定义特性:

1
HelpAttribute
阅读全文 »

C# 特性(Attribute)

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

规定特性(Attribute)

规定特性(Attribute)的语法如下:

1
2
[attribute(positional_parameters, name_parameter = value, ...)]
element

特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。

预定义特性(Attribute)

.Net 框架提供了三种预定义特性:

  • AttributeUsage

  • Conditional

  • Obsolete

AttributeUsage

预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。

规定该特性的语法如下:

1
2
3
4
5
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]

其中:

  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。

  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。

  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

例如:

1
2
3
4
5
6
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

Conditional

阅读全文 »

C#接口(Interface)

接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 “是什么” 部分,派生类定义了语法合同 “怎么做” 部分。

接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。

接口使得实现接口的类或结构在形式上保持一致。

抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。

接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约。

抽象类不能直接实例化,但允许派生出具体的,具有实际功能的类。

定义接口: MyInterface.cs

接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的。下面是一个接口声明的实例:

1
2
3
4
interface IMyInterface
{
void MethodToImplement();
}

以上代码定义了接口 IMyInterface。通常接口命名以 I 字母开头,这个接口只有一个方法 MethodToImplement(),没有参数和返回值,当然我们可以按照需求设置参数和返回值。

值得注意的是,该方法并没有具体的实现。

实现以上接口:InterfaceImplementer.cs

> 实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;

interface IMyInterface
{
// 接口成员
void MethodToImplement();
}

class InterfaceImplementer : IMyInterface
{
static void Main()
{
InterfaceImplementer iImp = new InterfaceImplementer();
iImp.MethodToImplement();
}

public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
}

InterfaceImplementer 类实现了 IMyInterface 接口,接口的实现与类的继承语法格式类似:

1
class InterfaceImplementer : IMyInterface

继承接口后,我们需要实现接口的方法 MethodToImplement() , 方法名必须与接口定义的方法名一致。

阅读全文 »

await 关键字

在C#编程中,await 关键字用于异步编程,它通常与 async 关键字一起使用。await 用于暂停当前方法的执行,直到等待的异步任务完成。这样可以在不阻塞主线程的情况下执行耗时操作,如I/O操作或网络请求。

例如,假设有一个异步方法 GetDataAsync(),你可以这样使用 await:

1
2
3
4
5
6
public async Task<string> FetchDataAsync()
{
// 假设 GetDataAsync 是一个返回 Task<string> 的异步方法
string data = await GetDataAsync();
return data;
}

在这个例子中,await GetDataAsync() 会暂停 FetchDataAsync 方法的执行,直到 GetDataAsync 完成并返回结果。同时,控制权会返回给调用者,允许程序继续执行其他操作。

在C#中使用await时,有几个关键要点需要注意,以确保异步代码的正确性和高效性:

async 方法必须返回 Task 或 Task<T>

使用 await 的方法必须标记为 async,并且返回类型应为 Task(无返回值)或 Task<T>(有返回值)。

例如:

1
2
3
4
5
public async Task<int> CalculateAsync()
{
await Task.Delay(1000); // 模拟耗时操作
return 42;
}

避免阻塞调用

不要在 async 方法中使用 .Result 或 .Wait(),这会导致死锁或阻塞线程。

例如,避免以下写法:

1
var result = SomeAsyncMethod().Result; // 错误:可能导致死锁

正确处理异常

异步方法中的异常会被包装在 Task 中,可以通过 try-catch 捕获。

例如:

1
2
3
4
5
6
7
8
try
{
await SomeAsyncMethod();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
阅读全文 »

Mesh 网格

Mesh Filter

官方手册地址:Mesh Filter

Mesh Filter 组件包含对网格的引用。该组件与同一个游戏对象上的 Mesh Renderer 组件配合使用;Mesh Renderer 组件渲染 Mesh Filt组件引用的网格。用于将网格数据应用到 3D 模型上。它是实现 3D 模型的重要组成部分之一,可以定义模型的形状和结构。

Mesh Filter 可以将网格数据应用到 3D 模型上,从而定义模型的形状和结构。它通过读取网格数据,包括顶点、三角形面片等信息,来定义模的形状和结构。这些网格数据可以通过外部工具(例如 Blender、Maya 等)创建,也可以通过代码动态生成。

Mesh Filter 提供了许多属性和方法,例如网格数据、法线、UV 坐标等,可以用来控制模型的形状和结构。另外,Mesh Filter
还支持动态修改网格数据,从而实现更加灵活的模型变形效果。

Mesh Renderer

官方手册地址:Mesh Renderer

Mesh Renderer 组件用于渲染网格。该组件与同一个游戏对象上的 Mesh Filter 组件配合使用;Mesh Renderer 组件渲染 Mesh Filter 组件引用的网格。

用于将 3D 模型渲染到屏幕上。它是游戏中 3D 模型的重要组成部分之一,可以让游戏中的角色、场景和物品等立体化呈现。

Skinned Mesh Renderer

官方手册地址:Skinned Mesh Renderer

Skinned Mesh Renderer 组件渲染一个可变形的网格。可变形网格包括蒙皮网格(具有骨骼和绑定姿势的网格)、具有混合形状的网格和运行布料模拟的网格。用于将骨骼动画应用到 3D 模型上。它是实现角色动画的重要组件之一,可以让游戏角色在运动中更加自然和流畅。

Skinned Mesh Renderer 可以在 3D 模型上应用骨骼动画,使得模型能够根据动画数据进行变形,从而实现角色的动画效果。它使用骨骼系统来管理骨骼和骨骼权重,并将这些信息应用到 3D 模型上,从而实现动画的变形效果。

Skinned Mesh Renderer 提供了许多属性和方法,例如骨骼系统、骨骼权重、动画剪辑、动画速度等,可以用来控制动画的播放和效果。另外,Skinned Mesh Renderer 还支持动态修改骨骼权重,从而实现更加灵活的动画效果。

Skinned Mesh Renderer 是 Unity 中实现角色动画的重要组件之一,可以让游戏角色在运动中更加自然和流畅。它是游戏开发中不可或缺的工具,可以提高游戏的可玩性和视觉效果。

Text Mesh

官方手册地址:Text Mesh

用于将文本转换为 3D 网格,以便进行高效的渲染和交互。与 Unity 自带的 Text 组件和 TextMeshPro-Text 组件相比,Text Mesh 更加轻量级和高效,适用于需要大量文本渲染的场景。

Text Mesh 可以用来渲染各种文本内容,例如游戏中的标签、计分板、物品名称等等。它能够将文本转换为 3D 网格,并使用 GPU 进行加速渲染,从而能够高效地处理大量文本内容。

Text Mesh 组件提供了许多属性,例如字体、字体大小、颜色、对齐方式、行距、字间距等,可以用来调整文本的排版和格式化。另外,Text Mesh 还支持动态生成文本,可以通过代码来实现动态更新文本内容,从而满足游戏中各种动态文本渲染需求。

Text Mesh 是 Unity 中非常高效的文本渲染组件,适用于需要大量文本渲染的场景,例如大量的标签、计分板、物品名称等等。

阅读全文 »

移动

当gameobject被加上rigidbody2d组件时,如果想移动它,不要这样写:

1
2
3
4
void Update()
{
this.transform.Translate (Vector3.left * speed * Time.deltaTime);
}

这种写法,主要针对无rigidbody2d的gameobject对象,否则,性能很差。
正确的写法:

1
2
3
4
5
6
7
8
9
void FixedUpdate()
{
rigidbody2D.MovePosition (rigidbody2D.position + Vector2.left * speed * Time.fixedDeltaTime);
}

void Start ()
{
rigidbody2D.velocity = Vector2.left * speed;
}

Body Type属性

rigidbody2d最为重要的一个属性,不同的选项,不同的物理效果:

Dynamic(动态的)

具有完全的物理特性;
会与所有类型的Rigidbody2D进行碰撞;
是最常用的Rigidbody2D类型、也是默认的类型;
是最耗费性能的类型;

Kinematic(运动学)

仅在用户控制下运动,不会受到重力和AddForce等力相关函数的影响;
只与Dynamic的Rigidbody2D发生碰撞(不勾选Use Full Kinematic Contacts);
不能通过力或碰撞改变速度,但是可以设置其速度和位置、旋转;
比Dynamic性能高;

Static (静态的)

具有无限质量、不可移动的物体;
velocity、AddForce、gravity、MovePosition、MoveRotation都不可用;
只与Dynamic的Rigidbody2D发生碰撞;
性能最高;

Simulated 物理模拟

阅读全文 »