在C#中,如果想根据类的字符串名动态生成类的示例对象,需要用到反射的知识。所谓反射,也就是利用程序集中的圆数据信息。凡是要用反射的程序,需要导入System.Reflection命名空间。
动态创建对象有两大类:Activator和Assembly。我更喜欢Activator类,先简单介绍下Assembly。
目录
1.1 假设你要反射一个 DLL 中的类,并且没有引用它(即未知的类型):
1.2 若要反射当前项目中的类(即当前项目已经引用它了)可以为:
1.Assembly的使用方法
1.1 假设你要反射一个 DLL 中的类,并且没有引用它(即未知的类型):
Assembly assembly = Assembly.LoadFile("程序集路径,不能是相对路径"); // 加载程序集(EXE 或 DLL)
dynamic obj = assembly.CreateInstance("类的完全限定名(即包括命名空间)"); // 创建类的实例
1.2 若要反射当前项目中的类(即当前项目已经引用它了)可以为:
Assembly assembly = Assembly.GetExecutingAssembly(); // 获取当前程序集
dynamic obj = assembly.CreateInstance("类的完全限定名(即包括命名空间)"); // 创建类的实例,返回为 object 类型,需要强制类型转换。
也可以这么使用:
Type type = Type.GetType("类的完全限定名");
dynamic obj = type.Assembly.CreateInstance(type);
2. Activator的用法
Activator 类提供好几个动态创建对象的重载方法。
1:public static object CreateInstance(Type type);
2:public static object CreateInstance(Type type, params object[] args);
其中关键在于获得一个Type类型的参数,有了这个参数才能创建一个对应类型的实例。而获取Type对象有三种方式
1:Type type = Type.GetType("命名空间名称.类名");
2:Type type = typeof("类名");
3:Person p = new Person(); //通过对象来进行创建type
Type type = p.GetType();
之所以更喜欢Activator是因为,它提供了带构造参数的方法,也就是可以将参数传递给构造函数。
3. 实例
假设有一个Animal基类,派生出三个类:Cat,Pig,Dog。给一个字符串数组,动态生成对应的实例对象。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace ReflectionTest
{
class Program
{
static void Main(string[] args)
{
List<string> classNames = new List<string> { "Dog", "Cat","Pig" };
Type t;
int i=1;
foreach (var s in classNames)
{
t = Type.GetType("ReflectionTest." + s);
object[] para = new object[] {"啦啦啦",i++};
Animal o = (Animal)Activator.CreateInstance(t, para);
//上面这句也可写成 Animal o = (Animal)Activator.CreateInstance(t, "啦啦啦", i++);
o.Bark();
}
}
}
public class Animal
{
public readonly int age = 0;
public readonly string name = "";
public Animal(string Name, int num)
{
age = num;
name = Name;
}
public virtual void Bark()
{
Console.WriteLine($" 姓名:{name} \n 年龄:{age}");
// Console.WriteLine($"ao,ao,ao!, hello ,my name is {name} and I am {age} years old!");
}
}
public class Dog:Animal
{
public Dog(string Name, int age) : base(Name, age) { }
public override void Bark()
{
Console.WriteLine($"我是一条狗,天天汪汪汪!");
base.Bark();
}
}
public class Cat:Animal
{
public Cat(string Name, int age) : base(Name, age) { }
public override void Bark()
{
Console.WriteLine("喵喵喵,爱吃鱼");
base.Bark();
}
}
public class Pig:Animal
{
public Pig(string Name, int age) : base(Name, age) { }
public override void Bark()
{
Console.WriteLine("哼哼哼,爱睡懒觉");
base.Bark();
}
}
}
运行结果如下:
我是一条狗,天天汪汪汪!
姓名:hhuihhjhjh
年龄:1
喵喵喵,爱吃鱼
姓名:hhuihhjhjh
年龄:2
哼哼哼,爱睡懒觉
姓名:hhuihhjhjh
年龄:3
请按任意键继续. . .
这里要说明一下,为什么要有一个公共的基类Animal,因为CreateInstance函数生成的对象类型默认为Object,需要强制转化为派生类,但是我们可能事先并不知道这个类的类型,但是知道它一定属于Animal类所以,可以这么做。反之,要是事先知道它是什么类型,就不需要动态生成了,直接使用new函数即可。
这也反映了一个局限:不同基类的类型,可以动态生成,但是没法转换为对应的类型。(或许是我没有找到可行的方法)