一:什么是反射
C#编写的程序会编译成一个程序集(.DLL或.exe),其中会包含元数据、编译代码和资源,通过反射可以获取到程序集中的信息
通俗来讲,反射就是我们在只知道一个对象的外部而不了解内部结构的情况下,可以知道这个对象的内部实现
二:反射的优缺点
首先在编译中分为动态编译和静态编译,静态编译是在编译中确定类型,绑定对象,而动态编译是在运行中确定类型,绑定对象
反射的优点:
——动态创建对象、绑定对象,提高了程序的灵活性和扩展性
——耦合度低
反射的缺点:增加时间开销
——反射是一种解释操作,在运行时解析方法或属性的名称
——反射需要处理元数据(metadata),例如获取类型信息、读取属性或方法的签名等
——反射操作通常不会被JIT编译器优化,因此它们不会受益于JIT编译器的内联优化和其他优化。此外,反射操作的结果通常不会被缓存
——反射操作需要进行类型安全检查,例如检查类型转换是否有效、检查调用权限等
三:通过Type类获取程序集、模块、类的相关信息
——Type类是一个抽象类,因此不能用他去实例化对象
——object类中定义了一个GetType方法,因此所有类都可以使用GetType()获取到某一个对象所属类的Type对象(有命名空间的话参数填命名空间.类名)
——通过Type对象可以获取到类中字段、属性、方法、构造函数等信息
——获取的时候可以通过BindingFlags筛选,注意BindingFlags.Static和BindingFlags.Instance这两个必须要使用一个,BindingFlags.NonPublic和BindingFlags.Public这两个必须要使用一个,默认是BindingFlags.Static|BindingFlags.Public|BindingFlags.Instance
using System;
class MainClass
{
public static void Main(string[] args)
{
//1.通过对象获取到这个对象所属类的Type对象
TestClass c = new TestClass();
Type t = c.GetType();
//2.通过Type类中的静态方法GetType获取到类的Type对象
//Type t = Type.GetType("TestClass");
//3.通过typeof关键字获取到类的Type对象
//Type t = typeof(TestClass);
Console.WriteLine(t.Name);//获取类名(不带命名空间)
Console.WriteLine(t.FullName);//获取类名(带命名空间)
Console.WriteLine(t.Assembly);//获取程序集
Console.WriteLine(t.BaseType);//获取基类类型
Console.WriteLine("----------获取类中字段");
var fields = t.GetFields();
foreach (var temp in fields)
{
Console.WriteLine(temp.Name);
}
Console.WriteLine("----------获取类中属性");
var properties = t.GetProperties();
foreach (var temp in properties)
{
Console.WriteLine(temp.Name);
}
Console.WriteLine("----------获取类中方法");
var methods = t.GetMethods();
foreach (var temp in methods)
{
Console.WriteLine(temp.Name);
}
Console.WriteLine("----------获取类中成员");
var members = t.GetMembers();
foreach (var temp in members)
{
Console.WriteLine(temp.Name);
}
Console.WriteLine("----------获取类中嵌套类");
var nesteds = t.GetNestedTypes();
foreach (var temp in nesteds)
{
Console.WriteLine(temp.Name);
}
Console.WriteLine("----------获取类中构造函数");
var constructors = t.GetConstructors();
foreach (var temp in constructors)
{
Console.WriteLine(temp.Name);
}
//获取所有程序集
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies()
}
}
public class TestClass
{
public string str;
public int num { get; set; }
public void Fun()
{
}
public class TestNestedClass
{
}
}
得到一个Type类型对象有三种方法:object.GetType()、Type.GetType()、typeof()
使用object.GetType()必须先创建一个实例,而后两种不需要创建实例,但使用typeof运算符仍然需要知道类型的编译时信息,Type.GetType()静态方法不需要知道类型的编译时信息它接收的是一个字符串(必须是全名,命名空间.类名)
四:创建实例:性能上要比new差
——通过Activator创建实例
using System;
class MainClass
{
public static void Main(string[] args)
{
object c = Activator.CreateInstance(typeof(TestClass), "hello world");
Console.WriteLine(((TestClass)c).str);
}
}
public class TestClass
{
public string str;
public TestClass(string str)
{
this.str = str;
}
}
——通过构造函数创建实例
using System;
class MainClass
{
public static void Main(string[] args)
{
Type t = typeof(TestClass);
Type[] paramTypes = new Type[1] { typeof(string) };
var info = t.GetConstructor(paramTypes);
object[] param = new object[1] { "hello world" };
var o = info.Invoke(param);
Console.WriteLine(((TestClass)o).str);
}
}
public class TestClass
{
public string str;
public TestClass(string str)
{
this.str = str;
}
}
五:反射赋值
using System;
using System.Reflection;
class MainClass
{
public static void Main(string[] args)
{
var data = Activator.CreateInstance(Type.GetType("TestClass"));
var info = data.GetType().GetField("str");
info.SetValue(data, "hello world");
Console.WriteLine(info.GetValue(data));
}
}
public class TestClass
{
public string str;
}
六:获取list中所有元素
using System;
using System.Reflection;
class MainClass
{
public static void Main(string[] args)
{
TestClass c = new TestClass();
c.list.Add(1);
c.list.Add(2);
Type type = c.list.GetType();
int count = Convert.ToInt32(type.InvokeMember("get_Count", BindingFlags.InvokeMethod, null, c.list, null));
for (int i = 0; i < count; i++)
{
object value = type.InvokeMember("get_Item", BindingFlags.InvokeMethod|BindingFlags.Default, null, c.list, new object[] { i});
Debug.Log(value);
}
}
}
public class TestClass
{
public List<int> list = new List<int>();
}
获取列表或数组中元素的类型:GetGenericArguments()[0]
七:调用类中的方法
——调用类中的方法
using System;
using System.Reflection;
class MainClass
{
public static void Main(string[] args)
{
var data = Activator.CreateInstance(Type.GetType("TestClass"));
var m1 = data.GetType().GetMethod("TestMethod1");
var m2 = data.GetType().GetMethod("TestMethod2");
m1.Invoke(data, null);
m2.Invoke(data, new object[] { 100});
}
}
public class TestClass
{
public void TestMethod1()
{
Console.WriteLine("this is TestMethod1");
}
public void TestMethod2(int i)
{
Console.WriteLine("this is TestMethod" + i);
}
}
——调用类中其他类的方法
例如调用一个类中List的Add方法
using System;
using System.Reflection;
using System.Collections.Generic;
class MainClass
{
public static void Main(string[] args)
{
var data = Activator.CreateInstance(Type.GetType("TestClass"));
var fi = data.GetType().GetField("list");
var list = Activator.CreateInstance(fi.FieldType);
data.GetType().GetField("list").SetValue(data, list);
var mi = list.GetType().GetMethod("Add");
var pi = list.GetType().GetProperty("Count");
mi.Invoke(list, new object[] { 1 });
mi.Invoke(list, new object[] { 2 });
Console.WriteLine(pi.GetValue(list));
}
}
public class TestClass
{
public List<int> list = new List<int>();
}