ディレクトリ
アセンブリの使用上の3つの記事の前に、型の種類を得るために、タイプ会員情報の種類を取得し使用。
以前の研究では、我々はおそらく組み立て、プレゼンスおよび出力情報PropertyInfo、のFieldInfo、ConstructorInfo、METHODINFO、ParameterInfo、EventInfo、のMemberInfoのことを学びました。
この記事から、一連の動作は、実際の反射、反射型をインスタンス化することによって開始します。
この記事では、デリゲートのインスタンスを、型のインスタンスに焦点を当てています。
1種類の例
タイプ(タイプ)モードからオブジェクトのインスタンスを作成し、両者はあります
- Activator.CreateInstance()メソッドは、操作種別のタイプ
- ConstructorInfo.Invoke()、動作している構成の手紙ConstructorInfo
タイプのインスタンスを作成する際、最初の型コンストラクタを考えます。
1.1 Activator.CreateInstance()
まず、マイクロソフトのドキュメントは、そのように定義され:
指定されたパラメータは、コンストラクタインスタンスの最高度に合わせて指定されたタイプが作成され使用してください。
それは何を意味するのでしょうか?
私たちは見てActivator.CreateInstance()
最も一般的に過負荷に使用される二つ。
object? CreateInstance(Type type);
object? CreateInstance(Type type, params object[] args);
コンストラクタに渡されたタイプをインスタンス化されたパラメータ引数、。オブジェクトを使用しているため、コンストラクタの最後のインスタンスを使用することで位置合わせ領域の程度最高。
まあ、引き、そして私たちは、実際にそれをしようとしないでください。
1.1.1単純型
Type typeA = typeof(int);
object objA = Activator.CreateInstance(typeA);
上記のコードを通して、私たちは簡単なタイプのインスタンスを作成することができます。
もちろん、あなたは型がオブジェクトの後に作成されていることがわかります。
だから、質問です
反射した後、ボクシングとアンボクシング欠かせない食事。
今のところ、我々はint型を使用することはできません、とだけオブジェクトを使用することができます。方法は?
まず解決聖歌に背後に保ちます。
もちろん、あなたが直接int型を使用することができ、その後、私はまた、なぜ反射でしょうか?
int i = 666;
これは、リストに載っていないのですか?
如果需要在程序生成后,引用 dll 的代码,我们可以这样做
Assembly ass = Assembly.LoadFrom(@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\System.Runtime.dll");
Type typeA = ass.GetType("System.Int32");
object objA = Activator.CreateInstance(typeA);
1.1.2 简单类型的构造函数
对于 int 这些简单类型,没有别的操作,直接实例化就行,这里例举 DateTime 类型,通过不同的参数,调用构造函数去实例化。
Type typeA = typeof(DateTime);
object objA = Activator.CreateInstance(typeA,2020,1,5);
当然,如果无法找到合适的构造函数来实例化类型,则会弹出 System.MissingMethodException
异常。
1.1.3 object
让我们创建一个类型
public MyClass(object a, object b)
{
}
public MyClass(string a, string b)
{
}
public MyClass(string a, object b)
{
}
通过反射创建实例
Type typeA = typeof(MyClass);
object objA = Activator.CreateInstance(typeA, 2020,666);
Console.WriteLine(typeA.Name);
以上代码并不会报错。
原因有两个,① 类型转换至 object,会携带原类型的信息;② Activator.CreateInstance() 会寻找最优的构造函数。
所以上面创建实例化时,会调用 public MyClass(int a, int b)
。没有符合的怎么办?那就调用最优解;
所以上面这点小意思,不会造成任何影响的。
对于简单类型,寻找过程如下
1,寻找相应类型的构造函数
Activator.CreateInstance(typeA, 2020,666)
,2020 是 typeo(int),666 是 typeof(int)。
最优是 public MyClass(int a, int b)
2,找不到的话,就找可以隐式转换的构造函数
例如 int -> long;
public MyClass(long a, long b)
3,如果没有隐式转换,则 object
public MyClass(object a, object b)
如果都没有符合条件的话,只能报错了;
验证一下
public class MyClass
{
public MyClass(string a, string b) { }
public MyClass(int a, int b) { }
}
class Program
{
static void Main(string[] args)
{
Type typeA = typeof(MyClass);
long a = 666;
long b = 666;
object objA = Activator.CreateInstance(typeA, a, b);
Console.WriteLine(typeA.Name);
Console.ReadKey();
}
}
不出意外的话,上面代码会报错。
1.1.4 故意出错
public class MyClass
{
public MyClass(string a = null) { }
public MyClass(StringBuilder a = null) { }
}
class Program
{
static void Main(string[] args)
{
Type typeA = typeof(MyClass);
object objA = Activator.CreateInstance(typeA, null);
Console.WriteLine(typeA.Name);
Console.ReadKey();
Console.ReadKey();
}
}
如无意外,上面的代码,执行后会报错。
因为当实例化时,参数为 null,有两个符合要求的构造函数。
其它情况下,根据 1.1.3 中,寻找构造函数的步骤,可以大致判断是否会出错。
1.1.5 Activator.CreateInstance() 性能
我们来通过正常的代码实例化一个类型,实声明并且赋值,共 1 千万次。
Stopwatch time = new Stopwatch();
time.Start();
for (int i = 0; i < 1_000_0000; i++)
{
int a = 666;
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds);
time.Reset();
time.Restart();
for (int i = 0; i < 1_000_0000; i++)
{
int a = 666;
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds);
时间
24
23
使用反射
Type typeA = typeof(int);
Stopwatch time = new Stopwatch();
time.Start();
for (int i = 0; i < 1_000_0000; i++)
{
object objA = Activator.CreateInstance(typeA);
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds);
time.Reset();
time.Restart();
for (int i = 0; i < 1_000_0000; i++)
{
object objA = Activator.CreateInstance(typeA);
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds);
时间
589
504
500 / 25 = 20,没错,性能相差了 20倍以上。
1.2 ConstructorInfo.Invoke()
ConstructorInfo.Invoke()
调用构造函数的限制性比Activator.CreateInstance()
高,并且是严格对应的。
1.1.4 中,故意出错的代码中,可以看到因为 null 时,有多个构造函数符合条件而导致程序报错。
使用 ConstructorInfo.Invoke()
创建实例进行测试。
public class MyClass
{
public MyClass(string a = null) { Console.WriteLine(6666); }
public MyClass(StringBuilder a = null) { }
}
class Program
{
static void Main(string[] args)
{
// 通过唯一性获取构造函数
// 通过参数类型和数量,获取到唯一的构造函数
ConstructorInfo conStruct = typeof(MyClass).GetConstructor(new Type[] { typeof(string) });
// 传输参数值并且进行实例化
object objA = conStruct.Invoke(new object[] { null });
Console.ReadKey();
}
}
使用 typeof(MyClass).GetConstructor(new Type[] { typeof(string) });
获取到类型的构造函数,然后使用 ConstructorInfo.Invoke()
实例化。
上面 GetConstructor()
的方法,重载定义如下
public ConstructorInfo? GetConstructor(Type[] types);
通过什么的方法,可以使用 public 构造函数实例化一个类型,如果想调用非 public 的构造函数呢?
可以使用 BindingFlags
,这些后面再慢慢学习。
2,实例化委托
使用 Delegate.CreateDelegate()
方法实例化一个委托,使用 Delegate.DynamicInvoke()
调用委托并且传递参数。
使用形式
CreateDelegate(Type, Object, MethodInfo)
Type 是此委托类型,Object 、MethodInfo 是实例类型、方法。
有两种情况,一种是实例方法、一种是静态方法。
我们创建一个委托以及类型
delegate int Test(int a, int b);
public class MyClass
{
public int A(int a, int b)
{
Console.WriteLine("A");
return a + b;
}
public static int B(int a, int b)
{
Console.WriteLine("B");
return a - b;
}
}
Main() 中 实验代码如下
// 绑定实例方法
Delegate d1 = Delegate.CreateDelegate(typeof(Test), new MyClass(), "A");
// 绑定静态方法
Delegate d2 = Delegate.CreateDelegate(typeof(Test), typeof(MyClass), "B");
Console.WriteLine(d1.DynamicInvoke(333,333));
Console.WriteLine(d2.DynamicInvoke(999,333));
Console.ReadKey();
输出
A
666
B
666
3,实例化泛型类型
3.1 实例化泛型
实例化一个泛型类型时,可以按照实例化普通类型过程操作
// 正常
Type type = typeof(List<int>);
object obj = Activator.CreateInstance(type);
// 下面的会报错
Type _type = typeof(List<>);
object _obj = Activator.CreateInstance(_type);
使用 Activator.CreateInstance
方法实例化一个泛型类型时,必须是 已绑定类型参数
的泛型 Type。
List<int>
已绑定 √; List<>
未绑定 ×。
另外,通过 ConstructorInfo.Invoke()
实例化也是一样的。
public class MyClass<T>
{
public MyClass(T a)
{
Console.WriteLine(a);
}
}
class Program
{
static void Main(string[] args)
{
// 正常
ConstructorInfo type = typeof(MyClass<int>).GetConstructor(new Type[] { typeof(int) });
object obj = type.Invoke(new object[] { 666 });
Console.ReadKey();
}
}
3.2 构造封闭泛型类型以及反转
3.2.1 构造封闭构造函数
有时候,传递过来的恰恰是 List<>
呢?
使用 Type.MakeGenericType(Type)
,
我们可以这样多一步,将未绑定类型参数的泛型 Type,转为封闭的 泛型 Type。
Type type = typeof(List<>);
// 构建泛型 Type
Type _type = type.MakeGenericType(typeof(int));
object _obj = Activator.CreateInstance(_type);
3.2.2 去除泛型类型的参数类型绑定
使用 Type.GetGenericTypeDefinition()
方法可以去除一个已绑定参数类型的泛型类型的参数类型。
Type type = typeof(List<int>);
Console.WriteLine(type.FullName);
// 构建泛型 Type
Type _type = type.GetGenericTypeDefinition();
Console.WriteLine(_type.FullName);
输出
System.Collections.Generic.List`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
System.Collections.Generic.List`1
List<int>
变成了 List<>
。
3.2.3 实践一下
上面介绍了泛型类型的实例化和两个关于参数类型的使用,下面来实践一下
static void Main(string[] args)
{
Type typeA = typeof(Console);
Type typeB = typeof(List<>);
Type typeC = typeof(List<int>);
去除泛型类型绑定的参数类型(typeA);
去除泛型类型绑定的参数类型(typeB);
去除泛型类型绑定的参数类型(typeC);
Console.ReadKey();
}
/// <summary>
/// 将 List<T> 转为 List<>
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static (bool, Type) 去除泛型类型绑定的参数类型(Type type)
{
// 检查是否泛型类型
if (type.IsGenericType == false)
{
Console.WriteLine("此类型不是泛型类型");
return (false, type);
}
// 检查是否是未绑定参数类型的泛型类型
if (type.IsGenericTypeDefinition)
{
Console.WriteLine("本来就不需要处理");
return (true, type);
}
Type _type = type.GetGenericTypeDefinition();
Console.WriteLine("处理完毕");
return (true, _type);
}
}