反射、特性、接口、属性综合例子

  1. 例子说明

现需给一个婴儿车编写主体程序,这个婴儿车的功能主要有:

  1. 上面有按键,输入数值,然后发出数值对应动物的声音。比如按下1,婴儿车就发出狗叫的声音
  2. 声音文件或声音逻辑程序(Dll文件)需要放在一个文件夹Animals下,这个逻辑程序需要放在函数名称为Voice的函数下
  3. 主体程序需要支持第三方插件,第三方只要把相关的声音文件或声音逻辑程序(Dll文件)放在Voice文件夹下,婴儿车也可以发出第三方指定的声音
    1. 主体程序实现

下面是主体程序,主要原理就是遍历Animals文件夹里的所有dll的所有类,找到有Voice方法的类。然后通过反射技术创建类对象并调用其Voice函数

//获取声音文件的路径

var folder = Path.Combine(Environment.CurrentDirectory, "Animals");

//获取所有文件

var files = Directory.GetFiles(folder);

var AnimalType = new List<Type>();

foreach(var mfile in files)

{

//加载文件程序集

var assembly = Assembly.LoadFile(mfile);

//获取程序集的所有类

var types = assembly.GetTypes();

//遍历所有类,把类中有Voice方法的类添加到数组

foreach (var t in types)

{

if (t.GetMethod("Voice") != null)

{

AnimalType.Add(t);

}

}

}

//下面是操作面板逻辑

while (true)

{

for(int i = 0; i < AnimalType.Count; i++)

{

Console.WriteLine($"{i+1}.{AnimalType[i].Name}");

}

Console.WriteLine("========================");

Console.WriteLine("请选择动物!");

int index = int.Parse(Console.ReadLine());

if (index > AnimalType.Count || index < 1)

{

Console.WriteLine("输入有误,请重新输入!");

continue;

}

Console.WriteLine("请输入次数!");

int times = int.Parse(Console.ReadLine());

//通过反射根据输入的数字获得对应的类

var t = AnimalType[index - 1];

//通过反射获得这个类下的Voice方法

var m = t.GetMethod("Voice");

//通过反射创建对象

var o = Activator.CreateInstance(t);

//通过反射传入参数调用方法

m.Invoke(o, new object[] {times});

}

  1. 第三方程序开发

上面已经往常主体程序开发,婴儿车出厂就自带有这个主体程序。现在新建一个VS项目,模拟第三方已经拿到主体程序,需要给他开发各种动物声音。

  1. 新建第一个动物Cat类库项目,并新建类

    public class Cat

    {

    public void Voice(int times)

    {

    for (int i = 0; i < times; i++)

    {

    Console.WriteLine("miaomiaomiao喵");

    }

    }

    }

  2. 新建第二个动物Dog类库项目,并新建类

    public void Voice(int times)

    {

    for (int i = 0; i < times; i++)

    {

    Console.WriteLine("wanwanwan汪");

    }

    }

  3. 结构如下:

  4. 编译后会在对应目录下生成两个dll文件,分别是:Cat.dll、Dog.dll
  5. 现在第三方已经生产了两种动物的声音,现在把它拷贝到主体程序的Animals文件夹下

  6. 运行主体程序,可以正常运行

    1. 改进1

站在开发主体程序的第一方来考虑问题,此项目已经基本完成,可以正常运行起来。但是主体程序是通过反射找到Voice方法来实现调用的,假如第三方假如由于粗心,把Voice写成了voice或者vioce等错误,那么主体程序是无法识别的。为了解决这个问题,开发主体程序的第一行就需要引用接口,然后写成一个SDK给第三方,第三方必须按照这个接口规则来写,就可以杜绝以上问题。下面是第一方程序的改进

  1. 新建一个单独的项目,生成类库,写一个interface

    public interface IAnimal

    {

    void Voice(int times);

    }

  2. 结构:

  3. 它会生成一个AnimalVoiceSDK.dll,后面把这个dll发给第三方,让第三方开发时必须继承这个接口,按照这个接口的规则生成类。
  4. 主体程序由于引入了接口,也可以做一些优化
  5. 优化1:遍历第三方的类时,通过反射获取到类的继承类,假如该继承类是我们发给第三方的Ianimal接口才继续。

    if (t.GetInterfaces().Contains(typeof(IAnimal)) ==true &&

    t.GetMethod("Voice") != null)

    {

    AnimalType.Add(t);

    }

    优化前:

    if (t.GetMethod("Voice") != null)

    {

    AnimalType.Add(t);

    }

  6. 优化2:反射创建对象时,是用的Object对象类型,假如引用了接口,则不需要强类型了

//通过反射创建对象

IAnimal o = (IAnimal)Activator.CreateInstance(t);

//直接调用方法

o.Voice(times);

优化前:

//通过反射创建对象

var o = Activator.CreateInstance(t);

//通过反射传入参数调用方法

m.Invoke(o, new object[] {times});

  1. 第三方拿到这个SDK后,需要引用到他们的项目里面去,下面的改进是由第三方完成:
  2. 第三方项目中引用AnimalVoiceSDK.dll,并继承接口下的Ianimal

    public class Cat: IAnimal

    {

    public void Voice(int times)

    {

    for (int i = 0; i < times; i++)

    {

    Console.WriteLine("miaomiaomiao喵");

    }

    }

    }

  3. 由于继承了接口,假如把Voice写成了voice或者vioce等错误,软件会直接报错。这样就可以完全杜绝此问题

    1. 改进2

假如第三方在写Voice函数时,有一些没完成的,或者调试仍有问题的。直接给到婴儿车上会有问题。为了解决这个问题,开发主体程序的第一方需要提供一个属性,这个属性可以标识在Voice函数是,主体程序反射遍历方法时,若函数标识有属性的,则不添加跳过。

  1. 第一方在SDK中添加一个自定义属性,属性中可以不添加任何内容。因为只是简单的标识作用

    namespace AnimalVoiceSDK

    {

    public class UnfinishAttribute:Attribute

    {

    }

    }

  2. 第一方修改反射获得Voice方法,标识有上面的属性的跳过

    if (t.GetInterfaces().Contains(typeof(IAnimal)) ==true &&

    t.GetMethod("Voice") != null)

    {

    var isUnfinishedAttribute = t.GetMethod("Voice").CustomAttributes.Any(a =>a.AttributeType == typeof(UnfinishAttribute));

    if (isUnfinishedAttribute == false)//没标识有未完成属性的才添加

    {

    AnimalType.Add(t);

    }

    }

  3. 然后把SDK发给第三方,假如第三方如下面标识,则婴儿车则不读取

    public class Cat: IAnimal

    {

    [UnfinishAttribute]

    public void Voice(int times)

    {

    for (int i = 0; i < times; i++)

    {

    Console.WriteLine("miaomiaomiao喵");

    }

    }

    }

 

 

 

 

 

 

 

 

 

 

 

posted @ 2022-04-30 19:06  ihh2021  阅读(97)  评论(0编辑  收藏  举报