C#反射

在前面简单学习了Linq To Object的常用标准查询运算符,在我们项目开发中,运用Linq表达式方便了我们编程,而接下来要讲的另一个内容——反射,也能很好地帮助我们处理某些特殊的情况。

 

一、反射的概念: 

反射提供了封装程序集、模块和类型的对象(Type类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。

一般使用:工厂类,通过反射创建类的实例,实现层与层之间的解耦:  数据层→数据会话层→业务逻辑层。 其中,数据会话层通过反射创建数据层的实例,业务逻辑层调用。

 

二、反射Type中的四个函数:


Person person = new Person();
Student student = new Student();
//判断指定的两个成员是否存在继承关系(判断一个类是否可用赋值给另一个类)    --后者继承于前者
bool b1 = typeof(Person).IsAssignableFrom(typeof(Student));    //student继承了person
bool b3 = person.GetType().IsAssignableFrom(student.GetType());//另一种写法
bool b2 = typeof(Itest).IsAssignableFrom(typeof(Teacher));

//判断是否是指定类的实例
bool b4 = typeof(Person).IsInstanceOfType(student);     //结果为true   student继承了person
bool b5 = person.GetType().IsInstanceOfType(student);//另一种写法   GetType当前对象的实例

//判断是否是某个类的子类,非接口
bool b6 = typeof(Student).IsSubclassOf(typeof(Person));

//判断是否是抽象
if (typeof(Drive).IsAbstract)
{
    Console.WriteLine("是抽象的");
}
else
{
    Console.WriteLine("不是抽象的");
}
Console.ReadKey();

 

 

要使用到的类和接口:

    class Person{}
    class Student:Person{}
    class Teacher:Itest{}
    interface Itest{}
    public abstract class Drive{}

三、反射中常用类的使用:

需求:通过反射获得Common程序集中的成员,并使用成员。

Comman程序集:

public class FileCommon
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public char Gender { get; set; }
        public FileCommon(string name)
        {
            this.Name = name;
        }
        public void WriteData(string path,string content)
        {
            File.WriteAllText(path, content, Encoding.Default);
        }
    }
    
 

1、把Common.dll放到该应用程序的bin/Debug目录下

string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"Common.dll");
Assembly ass = Assembly.LoadFile(path);  //需要绝对路径  LoadFile加载路径程序集内容        -->也不一定要在debug目录下,自己构建绝对路径也可
Type[] types = ass.GetTypes();             //获得加载到的程序集中的所有数据类型,包括公开和不公开的    
//Type[] types = ass.GetExportedTypes();   //获取加载到的公开程序集中的数据

 

2、可以遍历程序集中的类型,获取类型的命名空间和类型名称

foreach (Type item in types)
{ 
    Console.WriteLine(item.Name);             //类型名称  --也就是common下类或者接口的名称
    Console.WriteLine(item.Namespace);        //命名空间
}

3、获取指定程序集中的数据类型

    //获得指定程序集中数据类型,包括公开的和不公开的
    Type type = ass.GetType("Common.FileCommon");
    Console.WriteLine(type.Name);
    Console.WriteLine(type.Namespace);

 

4、创建type对象

      a:创建没有构造函数的对象

object o = ass.CreateInstance("Common.FileCommon");     //FileCommom为Common命名空间的一个类 → 命名空间.类名

    b:反射出来的类型有构造函数

object o2 = Activator.CreateInstance(type, "参数");

注意: 当反射出来的类型如果有构造函数,用上面代码中 ass.CreateInstance则会出现错误,如果有构造函数,那么该如何知道构造函数的参数?

ConstructorInfo [] info = type.GetConstructors();    //查询所有的构造函数,可以看到构造函数需要传递参数的参数类型

5、获得数据类型中所有的属性

 //获得数据类型中所有的属性
PropertyInfo[] pinfo = type.GetProperties();   //获取属性  然后可以遍历
foreach (PropertyInfo item in pinfo)
{
    Console.WriteLine(item.Name);
}

 

 

 

6、获得数据类型中所有的函数

//获得数据类型中所有的方法函数
MethodInfo[] minfo = type.GetMethods();   //获取所有的函数
foreach (MethodInfo item in minfo)
{
    Console.WriteLine(item.Name);
}

7、目的:调用函数

//调用WriteData函数
MethodInfo method = type.GetMethod("WriteData");    //该类中的writeData方法
object o3 = method.Invoke(o2, new object[] { "2.txt", "通过反射调用哒" });
Console.WriteLine("调用成功");
Console.ReadKey();

四、小结: 

当获得一个 .dll程序集的时候,需要先获取所有的类型,也就是这个程序集中的类,然后根据所需要类的名称,去创建指定名称的对象,如:Type type = ass.GetType("Common.FileCommon");  ,然后利用type获取构造函数、所有函数、属性(还有一些可以 type.方法名来获取相应的需求),再选择创建type对象的方法(构造或无构造),然后根据获得的函数来调用,传入相应的参数。

 

五、具体应用例子:

使用反射制作关机插件

1、先定义关机接口和具体关机的插件方法:

public interface IPlugin
{
    string Name { get; }
    //负责关闭计算机
    void GuanJi(int t);
}
public class ShutDownClass : IPlugin
    {
        public void GuanJi(int t)
        {
            //关机
            ShutDown(t.ToString());
            //返回一个K类型的默认值
        }

        public void ShutDown(string second)
        {
            Process process = new Process();
            process.StartInfo.FileName = "cmd.exe";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardInput = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.CreateNoWindow = true;
            process.Start();
            process.StandardInput.WriteLine("shutdown -s -f -t " + second);
            process.StandardInput.WriteLine("exit");

            process.Close();
            process.Dispose();
        }
        public string Name
        {
            get { return "关机"; }
        }
    }

 

 

     

 

2、窗体调用:

private void Form1_Load(object sender, EventArgs e)
        {
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plug");   //bin/debug/plug存放.dll文件

            string[] files = Directory.GetFiles(path);
            foreach (string item in files)
            {
                Assembly ass = Assembly.LoadFile(item);
                Type[] types = ass.GetExportedTypes();
                for (int i = 0; i < types.Length; i++)
                {
                    //判断是否为接口中的类,并不是抽象类
                    if (typeof(IPlugin).IsAssignableFrom(types[i]) && !types[i].IsAbstract)
                    {
                        //创建对象
                        o = Activator.CreateInstance(types[i]);
                        //获得指定对象
                        PropertyInfo pif = types[i].GetProperty("Name");
                        object o2 = pif.GetValue(o);
                        //添加到菜单栏
                        ToolStripItem tsi = PluginToolStripMenuItem.DropDownItems.Add(o2.ToString());
                        //把数据对象存储到要使用的对象
                        tsi.Tag = types[i];
                        //给添加的tsi注册单击事件
                        tsi.Click += (s, e2) =>
                        {
                            Type t = tsi.Tag as Type;
                            MethodInfo mi = t.GetMethod("GuanJi");
                            mi.Invoke(o, new object[] { 3000 });
                        };
                    }
                }
            }
        }

 

六、总结: 

很多.dll文件可以通过反射的方式来获取相应的类,类的方法属性的使用,一些应用程序也可以通过反编译软件来获取一些方法属性等。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C# 反射是指在运行时动态地获取类的信息,通过反射可以获取类的属性、方法、事件等信息,以及动态创建对象、调用方法、获取值等操作。这使得我们能够在运行时动态地编写代码,可以写出更加灵活和可扩展的程序。 C# 反射的核心是 `System.Reflection` 命名空间,该命名空间包含了许多与反射相关的类和接口,比如 `Type` 类、`MethodInfo` 类、`PropertyInfo` 类等。 下面是一些常用的反射操作: 1. 获取类型信息 可以使用 `typeof` 关键字或者 `Type.GetType()` 方法来获取类型的信息。`typeof` 关键字用于获取编译时已知的类型信息,而 `Type.GetType()` 方法则可以通过类型名称获取运行时的类型信息。 ```csharp // 获取 System.String 类型的信息 Type type1 = typeof(System.String); // 获取指定类型名称的信息 Type type2 = Type.GetType("System.String"); ``` 2. 获取成员信息 可以使用 `Type.GetMembers()` 方法获取类型的所有成员信息,包括属性、方法、字段、事件等。也可以使用 `Type.GetMethod()`、`Type.GetProperty()`、`Type.GetField()`、`Type.GetEvent()` 等方法获取指定成员的信息。 ```csharp Type type = typeof(Person); // 获取类型的所有成员信息 MemberInfo[] members = type.GetMembers(); // 获取指定属性的信息 PropertyInfo property = type.GetProperty("Name"); // 获取指定方法的信息 MethodInfo method = type.GetMethod("SayHello"); // 获取指定字段的信息 FieldInfo field = type.GetField("Age"); // 获取指定事件的信息 EventInfo eventInfo = type.GetEvent("PropertyChanged"); ``` 3. 动态创建对象 可以使用 `Activator.CreateInstance()` 方法动态创建对象,也可以使用 `Type.InvokeMember()` 方法调用构造函数来创建对象。 ```csharp Type type = typeof(Person); // 使用 Activator.CreateInstance() 方法创建对象 Person person1 = (Person)Activator.CreateInstance(type); // 使用 Type.InvokeMember() 方法调用构造函数创建对象 Person person2 = (Person)type.InvokeMember(null, BindingFlags.CreateInstance, null, null, new object[] { "Tom", 18 }); ``` 4. 调用成员 可以使用 `MethodInfo.Invoke()` 方法调用方法,也可以使用 `PropertyInfo.SetValue()` 方法设置属性的值,使用 `FieldInfo.SetValue()` 方法设置字段的值。 ```csharp Type type = typeof(Person); Person person = new Person("Tom", 18); // 调用方法 MethodInfo method = type.GetMethod("SayHello"); method.Invoke(person, null); // 设置属性的值 PropertyInfo property = type.GetProperty("Name"); property.SetValue(person, "Jerry", null); // 设置字段的值 FieldInfo field = type.GetField("Age"); field.SetValue(person, 20); ``` 以上是 C# 反射的一些基本操作,反射的应用非常广泛,可以用来实现插件式开发、ORM 映射等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值