02.接口与反射的在项目中的使用心得
一、简要解释
接口(Interface):
命名为I开头,用于实现面向对象编程中的多态特性,有利于程序的解耦
反射(Reflection):
可以根据内存中的类型参数动态的创建对应类型的实例,并访问其字段、属性及方法等
二、使用实例
接口实现多态:
class Program
{
static void Main(string[] args)
{
Ifactory ifactory = new CarFactory();//使用接口变量对实现接口的实例进行装箱
Console.WriteLine(ifactory.ProductInfo());//打印汽车工厂产品信息
ifactory = new FoodFactory();
Console.WriteLine(ifactory.ProductInfo());//打印食品工厂产品信息
Console.ReadLine();
}
}
/// <summary>
/// 工厂接口
/// </summary>
interface Ifactory
{
string ProductInfo();
}
/// <summary>
/// 实现工厂接口的汽车工厂类
/// </summary>
class CarFactory: Ifactory
{
public string ProductInfo()
{
return "汽车工厂生产的产品是各种汽车";
}
}
/// <summary>
/// 实现工厂接口的食品工厂类
/// </summary>
class FoodFactory : Ifactory
{
public string ProductInfo()
{
return "食品工厂生产的产品是各种食品";
}
}
使用接口及反射实现模块化(外挂式)编程和调用:
步骤A,创建接口项目文件生成接口dll文件
public interface ICar//接口
{
void Run(int run);
}
public class UnfinishedAttribute:Attribute//在接口项目中添加一个UnfinishedAttribute(类名可以自由命名)类并继承Attribute,在实现接口的方法中可以使用 [Unfinished]标识来标识未完成的接口实现
{
}
步骤B,创建ICar接口的实现类并生成对应的dll文件,并将dll文件放入到指定的文件夹内
//实现接口Car类
public class Car : ICar
{
public void Run(int run)
{
Console.WriteLine($"Car Run {run}!");
}
}
//实现接口Bus类
public class Bus : ICar
{
public void Run(int run)
{
Console.WriteLine($"Bus Run {run}!");
}
}
//实现接口Tank类
public class Tank : ICar
{
public void Run(int run)
{
Console.WriteLine($"Tank Run {run}!");
}
}
//未实现的实现接口BigTank类
[Unfinished]//未完成标识
public class BigTank : ICar
{
public void Run(int run)
{
Console.WriteLine($"BigTank Run {run}!");
}
}
步骤C,创建主程序这里使用控制台程序进行演示
步骤D,引用接口ICar
步骤E,使用Directory中的GetFiles方法获取指定目录( 存放dll的文件夹目录)下的所有的文件
var dataPath = Path.Combine(Environment.CurrentDirectory,"Data");//实现接口dll文件的存放目录路径
if (!Directory.Exists(dataPath))//如果Data文件夹不存在则创建
Directory.CreateDirectory(dataPath);
var files = Directory.GetFiles(dataPath);//获取目录下载所有文件
步骤F, 遍历每个文件并使用 Assembly中的LoadFile()方法来获取所有加载文件的程序集(Assembly)
Assembly assembly = Assembly.LoadFile(file);//加载遍历到的每一个dll文件
步骤G,遍历使用Assembly中的GetTypes()方法获取到的所有类型(Type)
var ts = assembly.GetTypes();//获取dll所有的数据类型
步骤H,根据需要判断每个类型是否包含指定的接口或其他的类型
if(t.GetInterfaces().Contains(typeof(ICar)))//判断每一个ts遍历项是否包含ICar实现的类型
步骤I:判断是否有未完成的实现类型,存在[Unfinished]属性标识的接口实现类
var isUnfinished=t.GetCustomAttributes(false).Any(a => a.GetType() == typeof(UnfinishedAttribute));//判断类型的属性为Unfinish
步骤J:使用反射创建实例并调用实例方法
object o= Activator.CreateInstance(currType);//根据数据类型使用反射创建实例
ICar car= o as ICar;//进行强制类型转换
car.Run(runLen);//调用Run方法
完整实现代码:
static void Main(string[] args)
{
var dataPath = Path.Combine(Environment.CurrentDirectory,"Data");
if (!Directory.Exists(dataPath))
{
Directory.CreateDirectory(dataPath);
}
var files = Directory.GetFiles(dataPath);
var dataType = new List<Type>();
foreach (var file in files)
{
Assembly assembly = Assembly.LoadFile(file);//加载dll文件
var ts = assembly.GetTypes();//获取dll所有的数据类型
foreach (var t in ts)
{
if(t.GetInterfaces().Contains(typeof(ICar)))//判断当前数据类型的所有继承或实现的接口是否包含ICar接口
{
var isUnfinished=t.GetCustomAttributes(false).Any(a => a.GetType() == typeof(UnfinishedAttribute));//判断类型的属性为Unfinish
if(!isUnfinished)
{
dataType.Add(t);
}
}
}
}
while (true)
{
Console.WriteLine("*********************************************************");
for (int i = 0; i < dataType.Count; i++)
{
Console.WriteLine($"{i + 1}. {dataType[i].Name}");
}
Console.WriteLine("请选择类型!");
int typeIndex = Convert.ToInt32(Console.ReadLine());
if (typeIndex < 1 || typeIndex > dataType.Count)
{
Console.WriteLine("没有找到选择的类型,请重试");
continue;
}
var currType = dataType[typeIndex - 1];//获取选中的数据类型
Console.WriteLine("请选择运行路程!");
int runLen = Convert.ToInt32(Console.ReadLine());
//强类型方式调用Run方法
object o= Activator.CreateInstance(currType);//根据数据类型使用反射创建实例
ICar car= o as ICar;//进行强制类型转换
car.Run(runLen);//调用Run方法
Console.WriteLine("*********************************************************");
}
}
反射扩展知识:
可以使用弱类型(object)实例调用实例方法,此种方式可能存在在调用实例方法时存在MethodInfo为null的异常的异常,因此不建议使用
实现代码:
MethodInfo mi = currType.GetMethod("Run");//获取当前数据类型中的Run方法,如果没有则此处为null
object obj = Activator.CreateInstance(currType);//根据数据类型创建object对象实例
mi.Invoke(obj, new object[] { runLen });//调用obhect对象实现run方法
完整代码:
static void Main(string[] args)
{
//定义路径
var dataPath = Path.Combine(Environment.CurrentDirectory, "Data");
if (!Directory.Exists(dataPath))
{
Directory.CreateDirectory(dataPath);
}
//加载外部dll
var files = Directory.GetFiles(dataPath);
var dataType = new List<Type>();
foreach (var file in files)
{
Assembly assembly = Assembly.LoadFrom(file);
var ts = assembly.GetTypes();
foreach (var t in ts)
{
if (t.GetMethod("Run") != null)//过滤不包含Run方法的数据类型
{
var isUnfinished = t.GetCustomAttributes(false).Any(a => a.GetType() == typeof(UnfinishedAttribute));//判断类型的属性为Unfinish
if (!isUnfinished)
{
dataType.Add(t);
}
}
}
}
while (true)
{
Console.WriteLine("*********************************************************");
for (int i = 0; i < dataType.Count; i++)
{
Console.WriteLine($"{i + 1}. {dataType[i].Name}");
}
Console.WriteLine("请选择类型!");
int typeIndex = Convert.ToInt32(Console.ReadLine());
if (typeIndex < 1 || typeIndex > dataType.Count)
{
Console.WriteLine("没有找到选择的类型,请重试");
continue;
}
var currType = dataType[typeIndex - 1];//获取当前选中的数据类型
Console.WriteLine("请选择运行路程!");
int runLen = Convert.ToInt32(Console.ReadLine());
MethodInfo mi = currType.GetMethod("Run");//获取当前数据类型中的Run方法,如果没有则此处为null
//弱类型方式进行方法调用
object obj = Activator.CreateInstance(currType);//根据数据类型创建object对象实例
mi.Invoke(obj, new object[] { runLen });//调用obhect对象实现run方法
Console.WriteLine("*********************************************************");
}
}
Demo实例
水平有限,欢迎大家留言交流