C#基础——反射

通过System.Reflection命名空间中包含的类型,可以写代码来反射(或者说“解析”)这些元数据表。实际上,这个命名空间中的类型为程序及或模块中包含的元数据提供了一个对象模型。
——《CLR via C#》

简单来说,反射就是程序通过读取“编译器创建的元数据表”来获得程序集、模块、类型本身信息(属性)的功能。

通过反射,程序可以获得对象的类型,动态地创建实例,访问其中的字段和属性,调用其中的方法。以及在运行时访问类型的特性【Attribute】(本文未提及)。

以下为使用C#的反射特性时常用的接口和实现方式:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace Reflection
{
public class Class
{
public class Student
{
public string Name { get; set; }
}
}

//基类
class BaseClass
{
public int BaseField = 0;
}
//派生类
class DerivedClass : BaseClass
{
public int DerivedField = 0;
}

public class InvokeClass
{
private string _testString;
private long _testInt;

public InvokeClass()
{

}

public InvokeClass(string abc)
{
_testString = abc;
}
public InvokeClass(StringBuilder abc)
{
_testString = abc.ToString();
}

public InvokeClass(string abc, long def)
{
_testString = abc;
_testInt = def;
}

public void TestMethod()
{
Console.WriteLine("我调用了InvokeClass的TestMethod方法");
}

public string TestFormat(string content)
{
return $"我调用了InvokeClass的TestFormat方法,传入了{content}参数";
}
}

class Program
{
delegate int delegateOperate(int a, int b);

private static void PrintTypeName(Type t)
{
Console.WriteLine($"NameSpace: {t.Namespace}");
Console.WriteLine($"Name :{t.Name}");
Console.WriteLine($"FullName: {t.FullName}");
}

public static int StaticSum(int a, int b)
{
return a + b;
}
public int InstanceSum(int a, int b)
{
return a + b;
}

static void Main(string[] args)
{
#region 获取类型和其中的字段

var bc = new BaseClass();
var dc = new DerivedClass();
BaseClass[] bca = new BaseClass[] { bc, dc };
foreach (var v in bca)
{
//获取类型
Type t = v.GetType();
Console.WriteLine("Object Type: {0}", t.Name);
//获取类中的字段
FieldInfo[] fi = t.GetFields();
foreach (var f in fi)
Console.WriteLine(" Field:{0}", f.Name);
Console.WriteLine();
}
Console.WriteLine("End!\n");
Console.ReadKey();

/*
输出结果:

Object Type: BaseClass
Field:BaseField

Object Type: DerivedClass
Field:DerivedField
Field:BaseField

End!
*/

#endregion

#region 获取数组类型

var intArray = typeof(int).MakeArrayType();
var int3Array = typeof(int).MakeArrayType(3);

Console.WriteLine($"是否是int 数组 intArray == typeof(int[]) :{intArray == typeof(int[]) }");
Console.WriteLine($"是否是int 3维数组 intArray3 == typeof(int[]) :{int3Array == typeof(int[]) }");
Console.WriteLine($"是否是int 3维数组 intArray3 == typeof(int[,,]):{int3Array == typeof(int[,,]) }");

//数组元素的类型
Type elementType = intArray.GetElementType();
Type elementType2 = int3Array.GetElementType();

Console.WriteLine($"{intArray}类型元素类型:{elementType }");
Console.WriteLine($"{int3Array}类型元素类型:{elementType2 }");

//获取数组的维数
var rank = int3Array.GetArrayRank();
Console.WriteLine($"{int3Array}类型维数:{rank }");
Console.WriteLine("End!\n");
Console.ReadKey();

/*
输出结果:

是否是int 数组 intArray == typeof(int[]) :True
是否是int 3维数组 intArray3 == typeof(int[]) :False
是否是int 3维数组 intArray3 == typeof(int[,,]):True
System.Int32[]类型元素类型:System.Int32
System.Int32[,,]类型元素类型:System.Int32
System.Int32[,,]类型维数:3
End!
*/

#endregion

#region 嵌套类型

var classType = typeof(Class);

foreach (var t in classType.GetNestedTypes())
{
Console.WriteLine($"NestedType ={t}");
//获取一个值,该值指示 System.Type 是否声明为公共类型。
Console.WriteLine($"{t}访问 {t.IsPublic}");
//获取一个值,通过该值指示类是否是嵌套的并且声明为公共的。
Console.WriteLine($"{t}访问 {t.IsNestedPublic}");
}

Console.WriteLine("End!\n");
Console.ReadKey();

/*
输出结果:

NestedType =Reflection.Class+Student
Reflection.Class+Student访问 False
Reflection.Class+Student访问 True
End!
*/

#endregion

#region 获取类型名称

var type = typeof(Class);
Console.WriteLine($"\n------------一般类型-------------");
PrintTypeName(type);

//嵌套类型
Console.WriteLine($"\n------------嵌套类型-------------");
foreach (var t in type.GetNestedTypes())
{
PrintTypeName(t);
}

var type2 = typeof(Dictionary<,>); //非封闭式泛型
var type3 = typeof(Dictionary<string, int>); //封闭式泛型

Console.WriteLine($"\n------------非封闭式泛型-------------");
PrintTypeName(type2);
Console.WriteLine($"\n------------封闭式泛型-------------");
PrintTypeName(type3);

Console.WriteLine("End!\n");
Console.ReadKey();

/*
输出结果:

------------一般类型-------------
NameSpace: Reflection
Name :Class
FullName: Reflection.Class

------------嵌套类型-------------
NameSpace: Reflection
Name :Student
FullName: Reflection.Class+Student

------------非封闭式泛型-------------
NameSpace: System.Collections.Generic
Name :Dictionary`2
FullName: System.Collections.Generic.Dictionary`2

------------封闭式泛型-------------
NameSpace: System.Collections.Generic
Name :Dictionary`2
FullName: System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
End!
*/

#endregion

#region 获取基类和所继承的接口

var base1 = typeof(System.String).BaseType;

foreach (var iType in typeof(Int32).GetInterfaces())
{
Console.WriteLine($"iType : {iType.Name}");
}

#endregion

//实际上,通过反射实例化对象、调用方法或委托、获取设置字段值的效率相当低下
//除此之外,通过反射使用方法或字段会因为避开了编译器检查而成为Bug之源
//日常需要避免使用

#region 实例化对象

//方法1,CreateInstance。但是如果类中没有参数列表对应的构造函数程序将报错
var dateTime1 = (DateTime)Activator.CreateInstance(typeof(DateTime),
2019, 6, 19);
Console.WriteLine($"DateTime1: {dateTime1}");

//方法2,使用ConstructInfo指定构造函数
//找到“只有一个参数为string”的构造函数
ConstructorInfo constructorInfo = typeof(InvokeClass).GetConstructor(
new[] { typeof(string) });
//使用该构造函数传入一个null参数
InvokeClass obj4 = (InvokeClass)constructorInfo.Invoke(
new object[] { null });

//可利用ConstructInfo查找对应的构造函数
ConstructorInfo[] constructorInfos =
typeof(InvokeClass).GetConstructors();
var constructorInfoArray2 = Array.FindAll(constructorInfos,
x => x.GetParameters().Length == 2);
//找到第二个参数是long类型的构造函数
var constructorInfo2 = Array.Find(constructorInfoArray2,
x => x.GetParameters()[1].ParameterType == typeof(long));
//如果存在,就创建对象
if (constructorInfo2 != null)
{
var obj5 = (InvokeClass)constructorInfo2.Invoke(
new object[] { "abc", 123 });
}

#endregion

#region 实例化委托

//静态方法的委托
Delegate staticD = Delegate.CreateDelegate(typeof(delegateOperate),
typeof(Program), "StaticSum");
//实例化对象方法的委托
Delegate instanceD = Delegate.CreateDelegate(typeof(delegateOperate),
new Program(), "InstanceSum");

Console.WriteLine($"staticD:{staticD.DynamicInvoke(1, 2)}");
Console.WriteLine($"instanceD:{instanceD.DynamicInvoke(10, 20)}");

#endregion

#region 泛型的实例化

Type closed = typeof(List<int>);
Type unBound = typeof(List<>);

//通过MakeGenericType把未封闭的泛型转为封闭的
Type newClosed = unBound.MakeGenericType(typeof(int));
//通过GetGenericTypeDefinition获得未封闭的泛型
Type newUnBound = closed.GetGenericTypeDefinition();

Console.WriteLine($"List<int> 类型{closed}");
Console.WriteLine($"List<> 类型{unBound}");
Console.WriteLine($"List<> MakeGenericType执行后 类型{newClosed}");
Console.WriteLine($"List<int> GetGenericTypeDefinition执行后 类型{newUnBound}");

#endregion

#region 调用类中方法

//获得一个类型的时候需要完整的"命名空间"+"类名"
//ex.CLR只知"System.Int32"不知"int"
classType = Type.GetType(typeof(InvokeClass).FullName);
InvokeClass instance = (InvokeClass)Activator.
CreateInstance(classType);

//通过反射调用的方法必须为public,否则返回null
//调用无参数的TestMethod
MethodInfo method = classType.GetMethod(
"TestMethod", new Type[] { });
method.Invoke(instance, null);

//调用有一个string参数的TestFormat方法
method = classType.GetMethod("TestFormat",
new Type[] { typeof(string) });
string result = method.Invoke(instance,
new object[] { "ViE" }).ToString();
Console.WriteLine(result);

Console.WriteLine("End!\n");
Console.ReadKey();

/*
输出结果:

iType : IComparable
iType : IFormattable
iType : IConvertible
iType : IComparable`1
iType : IEquatable`1
DateTime1: 2019/6/19 0:00:00
staticD:3
instanceD:30
List<int> 类型System.Collections.Generic.List`1[System.Int32]
List<> 类型System.Collections.Generic.List`1[T]
List<> MakeGenericType执行后 类型System.Collections.Generic.List`1[System.Int32]
List<int> GetGenericTypeDefinition执行后 类型System.Collections.Generic.List`1[T]
我调用了InvokeClass的TestMethod方法
我调用了InvokeClass的TestFormat方法,传入了ViE参数
End!
*/

#endregion
}
}
}