C# 反射 (Reflect)
1.基本内容
我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
最基本的调用:
Assembly assembly = Assembly.Load("DB.SqlServer");//将加载dll
Type type = assembly.GetType("DB.SqlServer.SqlServerHelper");//得到DLL 中的SqlServerHelper 类
object obj = Activator.CreateInstance(type);//创建类的实例
SqlServerHelper helper = (SqlServerHelper)obj;//将创建的Object 对象转换为SqlServerHelper对象
helper.Query();//调用对象的方法
2.创建对象
工厂方法
反射的一个应用场景是,当我们开发的程序的数据库是可能变化时,就会用到反射,入下述代码:
//这是一个DB接口层
namespace DB.Interface
{
public interface IDBHelper
{
void Query();
}
}
//这是SqlServer数据库的操作层
namespace DB.SqlServer
{
public class SqlServerHelper : IDBHelper//继承自DB接口层
{
public void Query()
{
Console.WriteLine("我是{0}", typeof(SqlServerHelper));
}
}
}
//这是MySql数据库的操作层
namespace DB.MySql
{
public class MySqlHelper : IDBHelper//继承自DB接口层
{
public void Query()
{
Console.WriteLine("我是{0}", typeof(MySqlHelper));
}
}
}
然后我们会建造一个工厂类,专门用于生产对象:
//这是工厂层
public class Factory
{
static string IDBHelperConfig = System.Configuration.ConfigurationManager.AppSettings["IDBHelperConfig"];
static string DllName = IDBHelperConfig.Split(',')[0];
static string TypeName = IDBHelperConfig.Split(',')[1];
public static IDBHelper CreateDBHelper()
{
Assembly assembly = Assembly.Load(DllName);//将加载dll
Type type = assembly.GetType(TypeName);//得到DLL 中的SqlServerHelper 类
object obj = Activator.CreateInstance(type);//创建类的实例
return (IDBHelper)obj;//将创建的Object 对象转换为IDBHelper对象 并返回
}
}
然后在app.config 文件中添加配置:
<appSettings>
<!--这是配置字符串-->
<add key="IDBHelperConfig" value="DB.SqlServer,DB.SqlServer.SqlServerHelper"/>
</appSettings>
最后在调用层面调用:
//这是调用
IDBHelper dbHelper = Factory.CreateDBHelper();
dbHelper.Query();
这样当数据库从SqlServer 修改为 MySql 时,我们只需要修改app.config中的配置字符串即可,而不需要修改源代码,这样有利于我们程序的维护,与稳定。
带参数对象创建
基础类
namespace Model
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get; set; }
public Person(int id)
{
Console.WriteLine("我是有1个参数的构造函数!");
this.Id = id;
}
public Person(int id, string name)
{
Console.WriteLine("我是有2个参数的构造函数!");
this.Id = id;
this.Name = name;
}
public Person(int id, string name, DateTime createDate)
{
Console.WriteLine("我是有3个参数的构造函数!");
this.Id = id;
this.Name = name;
this.CreateDate = createDate;
}
private Person()
{
Console.WriteLine("我是私有的,无参数构造函数");
}
}
}
有参数的构造函数调用方式:
Assembly assembly = Assembly.Load("Model");
Type personType = assembly.GetType("Model.Person");
//调用带1个参数的构造函数
object obj = Activator.CreateInstance(personType, new object[] { 123 });
//调用带2个参数的构造函数
object obj2 = Activator.CreateInstance(personType, new object[] { 123, "Oliver" });
//调用带3个参数的构造函数
object obj3 = Activator.CreateInstance(personType, new object[] { 123, "Oliver", DateTime.Now });
调用私有构造函数
Assembly assembly = Assembly.Load("Model");
Type personType = assembly.GetType("Model.Person");
//调用私有函数
object obj4 = Activator.CreateInstance(personType,true);
泛型类创建
基础类
namespace Model
{
//添加泛型类
public class GenericClass<T>
{
public GenericClass()
{
Console.WriteLine("我是泛型类的构造函数!");
}
public T GetT()
{
return default(T);
}
}
}
通过反射的方法创建泛型对象
Assembly assembly = Assembly.Load("Model");
Type gennericClassType = assembly.GetType("Model.GenericClass`1");//如果是一个泛型需要在后面添加`1,否则取出来的时NULL
Type newGennericClassType = gennericClassType.MakeGenericType(new Type[] { typeof(int) });
var obj = Activator.CreateInstance(newGennericClassType);
注意反射泛型类的时候GetType方法传入的类名称,需要在后面添加相应的泛型个数。下面这句代码也相应的说明这个情况。
//输出的值也会有一个`1
Console.WriteLine(typeof(GenericClass<int>));//输出:Model.GenericClass`1[System.Int32]
3.调用方法
基础类
namespace Model
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get; set; }
public string Memo;//字段
public Person(int id)
{
Console.WriteLine("我是有1个参数的构造函数!");
this.Id = id;
}
public Person(int id, string name)
{
Console.WriteLine("我是有2个参数的构造函数!");
this.Id = id;
this.Name = name;
}
public Person(int id, string name, DateTime createDate)
{
Console.WriteLine("我是有3个参数的构造函数!");
this.Id = id;
this.Name = name;
this.CreateDate = createDate;
}
private Person()
{
Console.WriteLine("我是私有的,无参数构造函数");
}
//带有返回值的无参方法
public DateTime Show()
{
Console.WriteLine("我是带有返回值的无参方法。");
return DateTime.Now;
}
//带有参数的方法
public void Show2(int i)
{
Console.WriteLine("我是Show2(int i).i=" + i);
}
//重载方法
public void Show3(int i)
{
Console.WriteLine("我是Show3(int i).i=" + i);
}
//重载方法
public void Show3(string s)
{
Console.WriteLine("我是Show3(string s).s=" + s);
}
//私有方法
private void Show4()
{
Console.WriteLine("我是私有方法");
}
//静态方法
public static void Show5()
{
Console.WriteLine("我是静态方法");
}
}
}
无参方法
Assembly assembly = Assembly.Load("Model");//加载dll
Type personType = assembly.GetType("Model.Person");//得到类
object objPerson = Activator.CreateInstance(personType, true);//创建对象
//调用 有返回值 无参数的方法
MethodInfo show = personType.GetMethod("Show");//找到方法
DateTime dt = (DateTime)(show.Invoke(objPerson, new object[] { }));//调用,并接收返回值
带参数方法
//调用 带有参数的方法
MethodInfo show2 = personType.GetMethod("Show2");
show2.Invoke(objPerson, new object[] { 123 });
重载方法
//调用 重载方法1
MethodInfo show3 = personType.GetMethod("Show3", new Type[] { typeof(int) });
show3.Invoke(objPerson, new object[] { 234 });
//调用 重载方法2
MethodInfo show3_1 = personType.GetMethod("Show3", new Type[] { typeof(string) });
show3_1.Invoke(objPerson, new object[] { "ABC" });
私有方法
//调用私有方法
MethodInfo show4 = personType.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic);
show4.Invoke(objPerson, new object[] { });
静态方法(两种形式)
//调用静态方法
MethodInfo show5 = personType.GetMethod("Show5");
show5.Invoke(objPerson, new object[] { });//类似于 实例调用
show5.Invoke(null, new object[] { });//类似于直接通过类名称调用
调用泛型类的泛型方法
基础类
namespace Model
{
//添加泛型类
public class GenericClass<T>
{
public GenericClass()
{
Console.WriteLine("我是泛型类的构造函数!");
}
public T GetT<S>(T t, S s)
{
Console.WriteLine("我是泛型类中的泛型方法!t_type:{0}, t_value:{1} s_type:{2}, s_value:{3}", typeof(T), t, typeof(S), s);
return t;
}
}
}
调用GenericClass类的GetT方法
Assembly assembly = Assembly.Load("Model");
Type genericClassType = assembly.GetType("Model.GenericClass`1");//`1 千万别忘记
Type newGenericClassType = genericClassType.MakeGenericType(new Type[] { typeof(int) });
object obj= Activator.CreateInstance(newGenericClassType);//创建对象
MethodInfo methodInfo = newGenericClassType.GetMethod("GetT");
MethodInfo newMethodInfo = methodInfo.MakeGenericMethod(new Type[] { typeof(string) });
newMethodInfo.Invoke(obj, new object[] { 123, "Oliver" });//输出:我是泛型类中的泛型方法!t_type:System.Int32, t_value:123 s_type:System.String, s_value:Oliver
4.get set 属性、字段
属性操作
Person p = new Person(1, "Oliver", DateTime.Now);
Type t = p.GetType();
PropertyInfo propertyInfo = t.GetProperty("Id");
Console.WriteLine(propertyInfo.GetValue(p));//获取 属性的值。输出 1
propertyInfo.SetValue(p,123);//获取 属性的值
Console.WriteLine(propertyInfo.GetValue(p));//设置 属性的值。输出 123
//遍历属性
foreach (var prop in t.GetProperties())
{
Console.WriteLine("{0}.{1}={2}", typeof(Person), propertyInfo.Name, prop.GetValue(p));
/*输出:
Model.Person.Id=123
Model.Person.Id=Oliver
Model.Person.Id=2018/8/8 22:15:09
*/
}
字段操作
Person p = new Person(1, "Oliver", DateTime.Now);
Type t = p.GetType();
FieldInfo fieldInfo = t.GetField("Memo");
Console.WriteLine(fieldInfo.GetValue(p));//获取 属性的值。输出 空字符串
fieldInfo.SetValue(p, "自律给我自由");//获取 属性的值
Console.WriteLine(fieldInfo.GetValue(p));//设置 属性的值。输出 自律给我自由