C#反射和特性

  • 什么是反射

反射是通过程序了解及调用“程序集”中程序相关的属性、字段、方法。

C#中程序集分两种:*.dll 和 *.exe两种。

  • 反射的作用

1、通过反射我们可以读取程序集(*.dll 和 *.exe)中代码内容

2、可以根类名(字符串),来动态创建类的对象

3、可以动态获取类对象中的属性、字段和方法

4、可以根据类中方法名(字符串)调用该方法

  • 反射常用类

1、Type类:可以获取类信息(即:获取类包含的成员与方法)

功能:使用Type对象查看一个类中所有的非私有字段、方法、属性

2、Assambly类:得到一个程序集中(反射)信息

功能:加载程序集

Assambly.Load(); //推荐使用

Assambly.LoadFrom("程序集的完整路径名");

下面建立一个控制台程序对这两个的类的用法进行演示 

定义一个Person类,我们用上述反射常用的两个类获取这个Person中的信息:

using System;
using System.Collections.Generic;
using System.Text;

namespace 反射学习
{
    public class Person
    {
        //字段
        public string Name;
        public int Age;
        private int _ID;

        //属性
        public int ID
        {
            get
            {
                return _ID;
            }
            set
            {
                _ID = value;
            }
        }

        //方法
        public void DisplayName()
        {
            Console.WriteLine(Name);
        }
        public void DisplayAge()
        {
            Console.WriteLine(Age);
        }

        public void DisplayID()
        {
            Console.WriteLine(ID);
        }

        /// <summary>
        /// 无参方法
        /// </summary>
        public void Test1()
        {
            Console.WriteLine("Person类 无参方法 Test1");
        }

        /// <summary>
        /// 有参方法
        /// </summary>
        /// <param name="str"></param>
        public void Test2(string str)
        {
            Console.WriteLine("Person类 有参方法 Test2,参数为=" + str);
        }

        /// <summary>
        /// 无参方法
        /// </summary>
        public void Test3()
        {
            Console.WriteLine("Person类 无参方法 Test3");
        }

        /// <summary>
        /// 重载方法
        /// </summary>
        /// <param name="num"></param>
        public void Test3(int num)
        {
            Console.WriteLine("有参方法Test3, 参数为=" + num);
        }

    }
}

使用Type类获取Person类中的信息(方法、属性、字段)

using System;
using System.Reflection;

namespace 反射学习
{
    class Program
    {
        /// <summary>
        /// Type 查询类的普通信息
        /// </summary>
        public void Test()
        {
            Person person = new Person();
            Type typeObj = person.GetType();

            //显示类的名称
            Console.WriteLine(typeObj.Name);

            //显示类命名空间
            Console.WriteLine(typeObj.Namespace);

            //显示类所属的程序集
            Console.WriteLine(typeObj.Assembly);          
        }

        /// <summary>
        /// Type 查询类中的方法、属性、字段等信息
        /// </summary>
        public void Test2()
        {
            Person person = new Person();
            Type typeObj = person.GetType();

            //得到字段信息
            FieldInfo[] fieldInfos = typeObj.GetFields();
            foreach (FieldInfo item in fieldInfos)
            {
                Console.WriteLine(item.Name);
            }
            Console.WriteLine("------------------------");

            //得到方法信息
            MethodInfo[] methodInfos = typeObj.GetMethods();
            foreach (MethodInfo item in methodInfos)
            {
                Console.WriteLine(item.Name);
            }
            Console.WriteLine("------------------------");

            //得到属性信息
            PropertyInfo[] propertyInfos = typeObj.GetProperties();
            foreach (PropertyInfo item in propertyInfos)
            {
                Console.WriteLine(item.Name);
            }
            Console.WriteLine("------------------------");
        }

        static void Main(string[] args)
        {
            Program pg = new Program();
            pg.Test();
            pg.Test2();
        }
    }
}

 使用Assembly类获取程序集中Person信息(类)

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace 反射学习
{
    class Program2
    {
        /// <summary>
        /// 通过程序集中的一个类的实例化,从而得到这个“程序集”的对象
        /// </summary>
        public void Test1()
        {
            Person person = new Person();
            //得到程序集
            Assembly assemblyObj = person.GetType().Assembly;
            //得到程序集的全名
            Console.WriteLine(assemblyObj.FullName);
            Type[] typeArray = assemblyObj.GetTypes();
            foreach (Type item in typeArray) //这里会输出三个类:Person、Program、Program2
            {
                Console.WriteLine(item.Name);
            }
        }

        /// <summary>
        /// 通过路径,直接访问“程序集”(*.dll)
        /// </summary>
        public void Test2()
        {
            //直接通过路径得到程序集对象
            Assembly assObj = Assembly.LoadFrom(@"D:\反射学习.dll");
            Type[] typeArray = assObj.GetTypes();
            foreach (Type item in typeArray) //这里会输出三个类:Person、Program、Program2
            {
                Console.WriteLine(item.Name);
            }
        }

        static void Main1(string[] args)
        {
            Program2 pg = new Program2();
            //pg.Test1();
            pg.Test2();
        }
    }
}

Test1()方法是通过Person对象获取该类所在的程序集,再通过该程序集获取获取所有类

Test2()方法是直接读取dll程序集中的所有类

 使用Assembly类获取程序集中指定类和指定的有参/无参方法及重载方法

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace 反射学习
{
    class Program3
    {
        /// <summary>
        /// 演示:调用程序集中类的无参方法
        /// </summary>
        public void Test1()
        {
            string className = "Person";                    //类名称
            string methodName = "Test1";                    //方法名称

            //得到程序集对象
            Assembly assembly = Assembly.LoadFrom(@"D:\反射学习.dll");
            //得到Type
            Type type= assembly.GetType("反射学习." + className);
            //实例化对象
            object obj = Activator.CreateInstance(type);
            //得到方法
            MethodInfo methodInfo = type.GetMethod(methodName);
            //调用无参方法
            methodInfo.Invoke(obj,null);  //null表示该方法无参

        }

        /// <summary>
        /// 演示:调用程序集中类的有参方法和重载方法
        /// </summary>
        public void Test2()
        {
            string className = "Person";
            string methodName1 = "Test2"; //Person类中Test2有参方法
            Assembly assembly = Assembly.LoadFrom(@"D:\反射学习.dll");
            Type type = assembly.GetType("反射学习." + className);
            object obj = Activator.CreateInstance(type);


            //Person类中Test2有参方法调用
            MethodInfo methodInfo1 = type.GetMethod(methodName1);
            object[] param1 = new object[1] { "123" };
            methodInfo1.Invoke(obj, param1);
        }


        /// <summary>
        /// 演示:调用程序集中类的有参方法和重载方法
        /// </summary>
        public void Test3()
        {
            string className = "Person";

            string methodName2 = "Test3"; //Person类中Test3无参方法
            string methodName3 = "Test3"; //Person类中Test3有参重载方法
            Assembly assembly = Assembly.LoadFrom(@"D:\反射学习.dll");
            Type type = assembly.GetType("反射学习." + className);
            object obj = Activator.CreateInstance(type);

            //Person类中Test3无参方法调用
            MethodInfo methodInfo2 = type.GetMethod(methodName2, new Type[0]);  //注意:有重载的无参方法,要加上new Type[0],否则程序不知道你要找的是哪一个
            methodInfo2.Invoke(obj, null);

            //Person类中Test3重载方法调用
            Type[] typeArrayPara = { typeof(int) };
            MethodInfo methodInfo3 = type.GetMethod(methodName3, typeArrayPara); //注意:有重载的有参方法,要加上{ typeof(int) },才能调用,否则程序报错
            object[] param2 = new object[1] { 456 };
            methodInfo3.Invoke(obj, param2);
        }

        /// <summary>
        /// 演示:调用程序集中的属性
        /// </summary>
        public void Test4()
        {
            string className = "Person";
            string propertyName = "ID";
            Assembly assembly = Assembly.LoadFrom(@"D:\反射学习.dll");
            Type type = assembly.GetType("反射学习." + className);
            object obj = Activator.CreateInstance(type);
            //获得属性
            PropertyInfo propertyInfo = type.GetProperty(propertyName);
            //给属性赋值
            propertyInfo.SetValue(obj, 23);
            //获得属性值
            string value = propertyInfo.GetValue(obj, null).ToString();
            Console.WriteLine(value);

        }

        static void Main(string[] args)
        {
            Program3 pg = new Program3();
            //pg.Test1();
            //pg.Test2();
            //pg.Test3();
            pg.Test4();
        }
    }
}

Test1():演示的是调用Person类中的无参发方法

Test2():演示的是调用Person类中的有参方法

Test3()和Test4():演示的是调用Person类中的重载方法

下面再额外演示反射三个功能:

1、使用反射查看谁是谁的父类,或者是否继承了接口

2、使用反射查看一个类是否是另一个类的实例

3、使用反射查看一个类是否为另一个类的子类 

先新建几个类:Men(父类)、ZhangSan(子类)、Dog(不相关的类)、ISpeak(接口)

Men.cs(父类):

/***************
 * 
 * Title:反射学习
 * 
 * Description:
 *            该类是个父类--人
 *            
 * Author:王亚东
 * 
 * Date:2024
 * 
 * Modify:
 * 
 * ****/

using System;
using System.Collections.Generic;
using System.Text;

namespace 反射学习
{
    class Men
    {
    }
}

 ZhangSan.cs(子类):

注意:此类继承了Men.cs和ISpeak接口

/***************
 * 
 * Title:反射学习
 * 
 * Description:
 *            该类是个子类,继承父类和接口
 *            
 * Author:王亚东
 * 
 * Date:2024
 * 
 * Modify:
 * 
 * ****/

using System;
using System.Collections.Generic;
using System.Text;

namespace 反射学习
{
    class ZhangSan:Men,ISpeak
    {
    }
}

Dog.cs(不相关类)

注:此类是一个不相关类,只是为了演示需要

/***************
 * 
 * Title:反射学习
 * 
 * Description:
 *            该类是一个普通,与其他父类和子类都不相关
 *            
 * Author:王亚东
 * 
 * Date:2024
 * 
 * Modify:
 * 
 * ****/

using System;
using System.Collections.Generic;
using System.Text;

namespace 反射学习
{
    class Dog
    {
    }
}

ISpeak.cs(接口类)

/*************
 * 
 * 
 * 接口:说话接口,由其他类继承
 * 
 * 
 * 
 * 
 * *************/
using System;
using System.Collections.Generic;
using System.Text;

namespace 反射学习
{
    interface ISpeak
    {
    }
}

 下面互相查看这几个的关系:

using System;
using System.Collections.Generic;
using System.Text;

namespace 反射学习
{
    class Program4
    {
        //查看谁是谁的父类,或者是否继承了接口
        public void Test1()
        {
            Type typeMen = typeof(Men);
            Type typeZhangSan = typeof(ZhangSan);
            Type typeDog = typeof(Dog);
            Type typeInterface = typeof(ISpeak);

            Console.WriteLine(typeMen.IsAssignableFrom(typeZhangSan)); //判断Men是否是ZhangSan类的父类,返回T
            Console.WriteLine(typeMen.IsAssignableFrom(typeDog)); //判断Men是否是Dog类的父类,返回F

            //接口判断
            Console.WriteLine(typeInterface.IsAssignableFrom(typeZhangSan));  //T
            Console.WriteLine(typeInterface.IsAssignableFrom(typeDog));  //F

        }

        //一个类是否为另一个类的实例
        public void Test2()
        {
            Type typeMen = typeof(Men);
            Type typeInterface = typeof(ISpeak);

            Men menObj = new Men();
            ZhangSan zhangsanObj = new ZhangSan();
            Dog dogObj = new Dog();

            Console.WriteLine(typeMen.IsInstanceOfType(menObj));    //menObj是否是typeMen的实例,返回T
            Console.WriteLine(typeMen.IsInstanceOfType(zhangsanObj)); //zhangsanObj是否是typeMen的实例,返回T
            Console.WriteLine(typeMen.IsInstanceOfType(dogObj));//dogObj是否是typeMen的实例,返回F
            //判断接口
            Console.WriteLine(typeInterface.IsInstanceOfType(menObj));//F
            Console.WriteLine(typeInterface.IsInstanceOfType(zhangsanObj));//T
        }

        //一个类是否为另一个类的子类
        public void Test3()
        {
            Type typeMen = typeof(Men);
            Type typeZhangSan = typeof(ZhangSan);
            Type typeDog = typeof(Dog);
            Type typeInterface = typeof(ISpeak);

            Console.WriteLine(typeZhangSan.IsSubclassOf(typeMen)); //判断ZhangSan类是否是Men的子类,返回T
            Console.WriteLine(typeDog.IsSubclassOf(typeMen)); //判断ZhangSan类是否是Men的子类,返回F
        }

        static void Main(string[] args)
        {
            Program4 pro4 = new Program4();
            //pro4.Test1();
            //pro4.Test2();
            pro4.Test3();
        }
    }
}

Test1():查看谁是谁的父类,或者谁继承了接口

Test2(): 查看一个类是否是另一个类的实例

Test3(): 查看一个类是否为另一个类的子类 

  • 什么是特性

特性是一种允许我们向程序的程序集中增加元数据的语言结构,提供了一种将声明性信息与代码关联起来的途径

  • 两个特性:Obsolete特性(即:“过时”特性)和Conditional系统特性(即:“条件”特性)
  •  Obsolete特性(即:“过时”特性):版本升级后API变更,对一些旧的API方法进行提示或强制更换,例如:在将Unity2018的项目升级到Unity2020版本后,API会有一些绿色下划线提示"某个方法已经过时,请用xxxx方法代替",或者直接红色下划线强制更换

代码如下

using System;

namespace 学习特性
{
    class Program
    {
        [Obsolete("此方法已过时,请用NewMethod方法替代",false)]  //第二个参数默认为false,非必须的,只是提示警告;当第二个参数设置为true时,此方法必须被替换,不替换会报错
        public void OldMethod()
        {
            Console.WriteLine("这是一个旧方法");
        }
        public void NewMethod()
        {
            Console.WriteLine("新的方法");
        }
        public void Test()
        {
            OldMethod();
            NewMethod();
        }
        static void Main(string[] args)
        {
            Program proObj = new Program();
            proObj.Test();
        }
    }
}

 [Obsolete("此方法已过时,请用NewMethod方法替代",false)] ://第二个参数默认为false,非必须的,只是提示警告;当第二个参数设置为true时,此方法必须被替换,不替换会报错 (红色下划线提示)

  •  Conditional系统特性(即:“条件”特性): 一般用于程序开发过程中的“测试方法”的编写。测试阶段定义“测试的宏”,发布商业版本,则取消宏即可。 适用条件:针对方法

代码如下

#define TestOnly  
#define debug

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace 学习特性
{
    class Program2
    {
        //特性:条件执行,默认不执行,只有在有“宏”的特定条件才执行
        [Conditional("TestOnly")]  //加上这个特性就不会被调用了,只能加上“宏”(上面的#define TestOnly  )才能被调用到,此特性是为了在测试环境或者编辑器环境下用的
        public void Test1()
        {
            Console.WriteLine("这是一个测试方法");
        }

        public void Method1()
        {
            Console.WriteLine("这是方法1");
        }

        public void Method2()
        {
            Console.WriteLine("这是方法2");
        }

        /// <summary>
        /// 学习演示“条件编译”
        /// </summary>
        public void Test2()
        {
            Console.WriteLine("aaaa");
            Console.WriteLine("bbbb");
#if debug
            Method1();
            Console.WriteLine("cccc");  //此代码不被执行,除非加了“宏”
#else
            Method2();
            Console.WriteLine("dddd");  //加了“宏”(即#define debug)之后此行代码不被执行,上一行就会被执行
#endif
        }
        public static void Main(string[] args)
        {
            Program2 pro2 = new Program2();
            //pro2.Test1();
            //pro2.Method1();
            //pro2.Method2();
            pro2.Test2();
        }
    }
}

Test1():加了 [Conditional("TestOnly")]特性后,该方法默认是不会被执行的,只能加上“宏”(上面的#define TestOnly  )才能被调用到

Test2():默认只执行#else和#endif之间的代码,除非加上“宏"(上面的#if debug)才会被执行

结语:好了,今天的反射和特性就讲解到这里,同学们若有疑问请在评论区留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值