前言:
此文章仅作本人学习笔记留存分享。如有出错欢迎指正。
反射(Reflection)的概念
.Net Framework 中提供了反射机制,可以再加载程序运行时,动态获取和加载程序集,并且可以获取到程序集的信息。 在程序集中,包含模块(Module),模块包含类型,类型包含成员,提供反射,我们可以查看到一个程序集的路径,命名空间,类。我们还可以对其进行操作。可以对程序集的类进行实例化,掉用类中的方法等,就跟我们普通使用程序集一样。
在Unity开发过程中,使用反射可以使代码更加灵活和可扩展,例如动态创建对象、动态调用方法和插件化开发等场景。
反射(Reflection)的用途
反射有下列用途:
- 它允许在运行时查看特性(attribute)信息。
- 它允许审查集合中的各种类型,以及实例化这些类型。
- 它允许延迟绑定的方法和属性(property)。
- 它允许在运行时创建新类型,然后使用这些类型执行一些任务。
优缺点
优点
-
动态加载和实例化对象:使用反射可以在运行时动态地加载和实例化对象,无需在编译时明确指定类型。这样可以提高代码的灵活性和可扩展性,使得代码更加适应变化和复杂性。
-
动态调用方法和属性:使用反射可以在运行时动态地调用对象的方法和属性,无需在编译时明确指定类型。这样可以使得代码更加适应变化和复杂性,同时也可以简化代码的编写。
-
插件化开发:使用反射可以实现插件化开发,即动态地加载和卸载插件。这样可以使得应用程序更加灵活和可扩展,同时也可以提高代码的复用性和可维护性。
缺点
-
性能开销:使用反射会带来一定的性能开销,因为需要在运行时进行类型信息的获取和转换。而且由于需要进行大量的类型检查和字符串匹配,所以在性能上会比直接调用类型和成员信息要慢。因此,在需要高性能的场景下,应尽量避免使用反射。
-
安全风险:由于反射可以在运行时动态地获取和调用类型和成员信息,所以在某些情况下可能会破坏代码的安全性。例如,可以使用反射来访问私有成员或者调用私有方法,这可能会导致代码的安全漏洞。因此,在使用反射时,需要特别注意代码的安全性。
-
代码可读性:使用反射会使代码的可读性变差,因为无法在编译时确定对象的类型和方法。如果使用不当,可能会导致代码难以理解和维护。
简单的使用场景
当我们需要动态地加载并实例化不同种类的角色。我们可以使用反射来实现这个功能。
using System;
using System.Reflection;
//基类
public abstract class MyProgram {
public string Actor { get; set; }
public abstract void DisPlay();
}
//派生类
public class ProgramA: MyProgram {
public override void DisPlay() {
Console.WriteLine("ProgramA");
}
private void DisPlayPrivate() {
Console.WriteLine("ProgramA DisPlayPrivate");
}
}
//派生类
public class ProgramB : MyProgram {
public override void DisPlay() {
Console.WriteLine("ProgramB");
}
}
//派生类
public class ProgramC : MyProgram {
public override void DisPlay() {
Console.WriteLine("ProgramC");
}
}
//使用反射(Reflection)来动态地加载并实例化不同的类
public class ProgramFactory {
public MyProgram CreateProgram(string className) {
Type type = Type.GetType(className);
if (type == null) {
Console.WriteLine("Cannot find class: " + className);
return null;
}
object instance = Activator.CreateInstance(type);
MyProgram character = instance as MyProgram;
if (character == null) {
Console.WriteLine("Invalid class type: " + className);
return null;
}
return character;
}
}
class Program {
static void Main(string[] args) {
Console.WriteLine("Use ProgramFactory");
string dllName = "Reflection";
//使用工厂
ProgramFactory factory = new ProgramFactory();
MyProgram programA = factory.CreateProgram($"{dllName}.ProgramA");
programA.DisPlay(); // output: ProgramA
MyProgram programB = factory.CreateProgram($"{dllName}.ProgramB");
programB.DisPlay(); // output: ProgramB
MyProgram programC = factory.CreateProgram($"{dllName}.ProgramC");
programC.DisPlay(); // output: ProgramC
//使用反射调用私有方法
Type type = Type.GetType($"{dllName}.ProgramA");
var ins = Activator.CreateInstance(type); //创建了一个ProgramA的实例
MethodInfo method = type.GetMethod("DisPlayPrivate", BindingFlags.NonPublic | BindingFlags.Instance);//获取方法
if (method != null) {
method.Invoke(ins, new object[] { });//执行方法
} else {
Console.WriteLine("method is null");
}
}
}
//更多案例
//https://viehere.fun/2021/02/13/Csharp%E5%9F%BA%E7%A1%80%E2%80%94%E2%80%94%E5%8F%8D%E5%B0%84/#more
//
执行结果:
使用反射的工厂模式与传统的工厂模式相比具有以下的优点:
- 灵活性:传统的工厂模式需要在代码中显式地列出所有可能创建的对象类型,而动态工厂模式使用反射可以在运行时动态地加载并实例化对象,使得代码更加灵活和可扩展。
- 可读性:使用传统的工厂模式需要编写大量的if/else语句或者switch语句,用于判断要创建的对象类型,代码可读性不高。而动态工厂模式使用反射可以使代码更加简洁、易读。
- 维护性:传统的工厂模式需要在代码中显式地列出所有可能创建的对象类型,如果需要添加或删除一个新的对象类型,需要修改代码并重新编译。而动态工厂模式使用反射可以动态地加载并实例化对象,不需要修改代码并重新编译,使得维护更加方便。
但是,反射需要在运行时动态地获取类型信息,并且使用反射创建对象的速度比直接创建对象要慢。因此,在实际开发中,需要权衡使用反射带来的灵活性和性能开销,选择合适的方案。
学习资料: