Net反射在项目中的应用
反射的概念和基本原理msdn很详细,这个文章主要说说反射在我的项目中的应用
反射用的比较多一个概念是程序集,也可以认为就是dll类库,程序集是所有类型的集合,它还有一个重要的东西就是元数据。JIT就是利用程序集的TypeRef和AssemblyRef等元数据来确定所引用的程序集 及类型,这些元数据包括名称、版本、语言文化和公钥标记等,JIT就是根据这些信息来加载一个程序集到应用程序域中。如果要自己加载一个程序集,可以调用 类型Assembly的LoadXXX系列方法。从Assembly中可以读到这个dll中所以类,类的继承接口,类的方法,属性,字段,事件等等。
反射和接口
反射是在运行中动态的创建需要的类,接口和接口的方法在编译的时候已经确定了,接口的实现依赖他的继承类,有了继承类,接口才能实例化使用定义好的方法。
反射就是把接口的实例化推迟到运行阶段。所以反射一般和接口搭配使用。
应用场景一:单个接口对应多个实现
这个场景比较多,而且在抽象工厂模式中我觉得用的很多,典型的例子是数据读取层。
一个项目可能用到SqlSever,Access,Orace或者Txt,XML来当存取数据,他们的方法都是统一,比如增,删,修,读等
这个时候就是定义一个IDataAccess接口,这个接口定义了统一的方法,增,删,修,读等,然后分别用不同的实现类来继承这个接口,
比如SqlServer类,XmL类,定义为SqlServerDataAccess,XMLDataAccess,他们都继承IDataAccess
在应用的时候项目可以通过简单的修改或者配置来使用Sqlserver或者XML数据库,这个时候就可以使用反射来决定接口IDataAccess到底使用哪个实现类
抽象工厂模式中使用配置文件来设置使用Sqlserver还是XML数据实现类。在配置文件中定义“程序集和命名空间类名”的信息,这样通过修改配置文件就可以决定使用
Sqlserver还是XML数据实现类
public IDataAccess CreateDatAccess()
{
IDataAccess IDA =(IDataAccess)Assembly.Load("配置节点程序集").CreateInstance("命名空间.Sqlserver");
//IDataAccess IDA =(IDataAccess)Assembly.Load("配置节点程序集").CreateInstance("命名空间.XML");
return IDA;
}
应用场景二:多个接口和多个实现类
这个例子的完全可以使用第一个场景的方案来解决,但是由于接口多,实现类,实现起来比较复杂
这个例子说的是多个接口,每个接口可能有一个实现类,也可能有多个实现类。
基本实现思路通过遍历bin下的文件夹,得到dll信息,把接口和对应的实现类组织到字典集合中,然后根据一个接口信息就可以得到实现类,实现接口的动态实例化
如果接口只有一个实现类就直接取得这个类,如果接口有多个实现类那就传递一个类的名称来明确要求读取哪个类
具体实现,为了更好的项目结构,建立一个接口dll,然后不同的接口对应不同的dll类库,实现类的项目名称最好有个规格,方便在遍历文件夹的时候读取特定名称的dll,加快遍历速度。
在ConsoleApplication2.Framework定义两个接口
public interface ICar
{
void Run();
}
public interface IProduct
{
void OutputName();
}
在ConsoleApplication2.Impl.Product定义产品接口实现类
public class ProductA : IProduct
{
public void OutputName()
{
Console.WriteLine("Product A Name...");
}
}
在ConsoleApplication2.Impl.Product定义产品接口实现类
public class AudiCar : ICar
{
public void Run()
{
Console.WriteLine("Audi Car Run...");
}
}
public class QQCar : ICar
{
public void Run()
{
Console.WriteLine("QQ Car Run...");
}
}
然后开始重点代码部分
1.建立接口和实现类的对应关系,保存到字典集合中
static Dictionary<Type, List<Type>> dictionary = new Dictionary<Type, List<Type>>();
public static void GetInterfaceAndType()
{
string s = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
foreach (var file in Directory.GetFiles(s, "ConsoleApplication2.Impl.*.dll"))//遍历程序下的类似命名规范的dll,
{
var ass = Assembly.Load(File.ReadAllBytes(file));//得到程序集dll
Console.WriteLine(ass.FullName);
foreach (Type type in ass.GetTypes().Where(p=>p.IsClass))//遍历程序集中类
{
Console.WriteLine(type.FullName);
Type[] interfaces = type.GetInterfaces();//该类继承的接口,可能是多个接口
foreach (Type inter in interfaces)//建立接口和实现类的对应关系,一个接口可能多个实现类
{
if (!dictionary.ContainsKey(inter))
{
dictionary.Add(inter, new List<Type>());
}
dictionary[inter].Add(type);
}
}
}
}
在根据接口读取实现类,因为接口不同,所以用泛型来实现
//specifiedImplType参数可以为空,如果一个接口有多个实现类的时候,需要特别指定使用哪个实现类
public static T GetImpTypeByInterface<T>(string specifiedImplType = "")
{
Type interfaceType = typeof(T);//接口
if (dictionary.Count > 0 && dictionary.ContainsKey(interfaceType))
{
Type implType = null;
if (specifiedImplType == "")//读字典集合中根据接口key得到实现类Type
{
implType = dictionary[interfaceType].First();
}
else
{
implType = dictionary[interfaceType].Where(p => p.Name == specifiedImplType).FirstOrDefault();
}
return Activator.CreateInstance(implType) as T;//Activator.CreateInstance该语法创建类的实例,并且As 转换为T类型
}
else
{
throw new Exception("没有继承对象");
}
}
最后测试运行
GetInterfaceAndType();//建立接口和实现类的对应集合
ICar iCar = GetImpTypeByInterface<ICar>();//默认第一个实现类
iCar.Run();
ICar iCar = GetImpTypeByInterface<ICar>(“QQCar”);//指定实现类
iCar.Run();
这样就可以直接根据接口类找到他对应的实现类,