C#之反射与特性篇

反射

定义

  • 类型 Type:描述程序集,类型等的元数据信息
  • 反射 Reflect:
    一个运行时获取元数据信息的过程,得到一个给定程序集所包含的所有类型的列表,包括给定类型的方法,字段,属性和事件,接口,方法的参数和其他相关细节(基类,命名空间,清单数据)

System.Type 类

  • 常用属性 IsClass IsEnum IsAbstrace IsValueType IsInterface
  • 常用方法 GetFields GetInterface GetMethods GetMembers
  • 获取实例 GetType

获取Type

  • Object.GetType()
TestObject o = new TestObject();
Type t = o.GetType();
  • typeof()
Type t = typeof(o);
  • System.Type.GetType()
Type t = Type.GetType("TestObject");

查看元数据

class MainClass
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            ListMethods2(args.GetType());

            ListMethods(args.GetType());

            ListFields(args.GetType());

            ListProps(args.GetType());

            ListInterface(args.GetType());

            ListVariousStats(args.GetType());
        }


        /// <summary>
        /// 反射方法 参数 和 返回值
        /// </summary>
        /// <param name="t"></param>
        static void ListMethods(Type t)
        {
            Console.WriteLine("*****Mthods****");
            MethodInfo[] mi = t.GetMethods();
            foreach (MethodInfo m in mi)
            {
                
                //得到返回类型
                string retVal = m.ReturnType.FullName;
                string paramInfo = "( ";
                //得到参数
                foreach(ParameterInfo pi in m.GetParameters())
                {
                    paramInfo += string.Format("{0} {1}", pi.ParameterType, pi.Name);
                }
                paramInfo += " )";

                Console.WriteLine("->{0} {1} {2}", retVal, m.Name, paramInfo);

            }

            Console.WriteLine();
        }

        /// <summary>
        /// getmethod toString()
        /// </summary>
        /// <param name="t"></param>
        static void ListMethods2(Type t)
        {
            Console.WriteLine("*****Methods2******");
            var methodName = from n in t.GetMethods() select n;
            foreach (var name in methodName)
                Console.WriteLine("->{0}", name);
            Console.WriteLine();
        }

        /// <summary>
        /// 反射字段
        /// </summary>
        /// <param name="t"></param>
        static void ListFields(Type t)
        {
            Console.WriteLine("*****Fields****");
            var fileldName = from f in t.GetFields() select f.Name;

            foreach (var name in fileldName)
                Console.WriteLine("->{0}", name);

            Console.WriteLine();
        }

        /// <summary>
        /// 反射属性
        /// </summary>
        /// <param name="t"></param>
        static void ListProps(Type t)
        {
            Console.WriteLine("*****Props****");
            var propName = from f in t.GetProperties() select f.Name;

            foreach (var name in propName)
                Console.WriteLine("->{0}", name);

            Console.WriteLine();
        }

        /// <summary>
        /// 反射接口
        /// </summary>
        /// <param name="t"></param>
        static void ListInterface(Type t)
        {
            Console.WriteLine("*****Interface****");
            var propName = from f in t.GetInterfaces() select f.Name;

            foreach (var name in propName)
                Console.WriteLine("->{0}", name);

            Console.WriteLine();
        }

        /// <summary>
        /// 显示其他信息
        /// </summary>
        /// <param name="t"></param>
        static void ListVariousStats(Type t)
        {
            Console.WriteLine("*****Various stats****");

            Console.WriteLine("Is type abstract? {0}", t.IsAbstract);
            Console.WriteLine("Is type sealed? {0}", t.IsSealed);
            Console.WriteLine("Is type IsGenericTypeDefinition? {0}", t.IsGenericTypeDefinition);
            Console.WriteLine("Is type IsClass? {0}", t.IsClass);
        }
    }

反射程序集

Assembly.Load 动态加载程序集,反射获取程序集的元数据

class MainClass
    {

        // 输入 文件名,不需要带.dll 后缀
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Console.WriteLine("***** External Assembly Viewer ****");
            string asmName = "";
            Assembly asm = null;

            do
            {
                Console.WriteLine("\n Enter as assembly to evaluate");
                Console.WriteLine("or enter Q to quit");

                asmName = Console.ReadLine();

                if (asmName.ToUpper() == "Q")
                {
                    break;
                }

                try
                {
                    asm = Assembly.Load(asmName);

                    DisplayTypesInAsm(asm);
                }
                catch
                {
                    Console.WriteLine("sorry, cant find assembly");
                }

            }
            while (true);
        }


        static void DisplayTypesInAsm(Assembly asm)
        {
            Console.WriteLine("\n**** Types in Assembly ****");

            Console.WriteLine("->{0}", asm.FullName);
            Type[] types = asm.GetTypes();
            foreach (Type t in types)
                Console.WriteLine("Type: {0}", t);

            Console.WriteLine("");
        }
    }

晚期绑定(late binding)

定义

创建一个给定类型的实例,并在运行时调用其方法,而不需要在编译期知道它存在的一种技术

方法

System.Activator.CreateInstance() 创建运行时实例

MethodInfo.Invoke() 执行实例的方法

    class MainClass
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Assembly a = null;

            try
            {
                a = Assembly.Load("hellodll2");
            }
            catch(Exception e)
            {
                Console.WriteLine(e);
                return;
            }

            if (a != null)
            {
                CreateUsingLateBinding(a);
            }

            Console.ReadLine();
        }

        static void CreateUsingLateBinding(Assembly asm)
        {
            try
            {
                Type myClass = asm.GetType("hellodll2.MyClass");

                object obj = Activator.CreateInstance(myClass);

                Console.WriteLine("Create a {0} using late binding!", obj);

                //调用无参数函数
                MethodInfo hello = myClass.GetMethod("hello");
                Console.WriteLine("call {0} ", hello.Invoke(obj, null));

                //调用包含参数的函数
                MethodInfo show = myClass.GetMethod("show");
                Console.WriteLine("call {0} ", show.Invoke(obj, new object[] { "liwen", 35}));

            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
    
namespace hellodll2
{
    public class MyClass
    {
        public MyClass()
        {
        }

        public static string hello()
        {
            return "hello world";
        }

        public static string show(string name, int age)
        {
            return string.Format("name:{0} age: {1}", name, age);
        }
    }
}

特性

C#常用特性

  • [Obsolete]
  • [Serializable]
  • [NonSerialized]

自定义特性

    /// <summary>
    /// 自定义特性 AttributeTargets:限定应用范围; Inherited是否能够给派生类集成
    /// </summary>
    [AttributeUsage( AttributeTargets.Field, Inherited =false)]
    public sealed class VehicleDescriptionAttribute : System.Attribute
    {
        public string Description { get; set; }

        public VehicleDescriptionAttribute(string description)
        {
            Description = description;
        }

        public VehicleDescriptionAttribute() { }
    }

使用特性

    [Serializable]
    public class Motorcycle
    {

        [NonSerialized]
        float weightOfCurrentPassengers;

        [Obsolete("use another vehicle")]
       public bool hasRadioSystem;

        [VehicleDescription("head set")]
        public bool hasHeadSet;

        bool hasSissyBar;

    }

反射获取特性

    class MainClass
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Motorcycle motorcycle = new Motorcycle();
            motorcycle.hasRadioSystem = true;

            motorcycle.hasHeadSet = true;

            ReflectOnAttributeUsageEarlyBinding();
        }


        /// <summary>
        ///  使用早绑定 反射特性
        /// </summary>
        private static void ReflectOnAttributeUsageEarlyBinding()
        {
            Type t = typeof(Motorcycle);
            object[] customAttr = t.GetCustomAttributes(false);

            foreach (var v in customAttr)
            {
                if (v is VehicleDescriptionAttribute)
                {
                    Console.WriteLine("-->{0}\n", ((VehicleDescriptionAttribute)v).Description);
                }
               
            }
               
        }
    }

Unity TNet 反射和特性的应用

  1. [RFC] 定义远程函数调用特性
  2. CachedFunc 封装MethodInfo.Invoke函数执行方法
  3. BuildMethodList 发射获取RFC 函数并保存到本地列表中
  4. 从本地列表中查询RFC函数执行
/// <summary>
/// TNet 反射 特性应用
/// 用RFC 特性
/// 利用反射机制 保存具有RFC的obj 和 函数
/// 然后网络层收到消息后执行函数
/// </summary>
namespace Lesson6
{
    class MainClass
    {


        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            MainClass ml = new MainClass();

            BuildMethodList(ml);

            CachedFunc ent = null;
            if (mDict1 != null) mDict1.TryGetValue("testRFC", out ent);
            ent.Execute();

            if (mDict1 != null) mDict1.TryGetValue("testRFC2", out ent);
            ent.Execute();

            if (mDict1 != null) mDict1.TryGetValue("testRFC3", out ent);
            ent.Execute();
        }

        [RFC]
        public void testRFC()
        {
            Console.WriteLine("call RFC");
        }

        [RFC]
        public void testRFC2()
        {
            Console.WriteLine("hello world");
        }

        [RFC]
        public void testRFC3(string hello)
        {
            Console.WriteLine("say " + hello);
        }

        [System.NonSerialized]
        static Dictionary<System.Type, System.Collections.Generic.List<CachedMethodInfo>> mMethodCache =
new Dictionary<System.Type, System.Collections.Generic.List<CachedMethodInfo>>();


        // Cached RFC functions
        [System.NonSerialized] static Dictionary<int, CachedFunc> mDict0 = new Dictionary<int, CachedFunc>();
        [System.NonSerialized] static Dictionary<string, CachedFunc> mDict1 = new Dictionary<string, CachedFunc>();

        /// <summary>
        /// 反射获取MainClass的RFC, 保存到列表中
        /// </summary>
        private static void BuildMethodList(MainClass mb)
        {
            var type = mb.GetType();

            System.Collections.Generic.List<CachedMethodInfo> ret;

            if (!mMethodCache.TryGetValue(type, out ret))
            {
                ret = new System.Collections.Generic.List<CachedMethodInfo>();
                var cache = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

                for (int b = 0, bmax = cache.Length; b < bmax; ++b)
                {
                    var ent = cache[b];
                    if (!ent.IsDefined(typeof(RFC), true)) continue;

                    var ci = new CachedMethodInfo();
                    ci.name = ent.Name;
                    ci.rfc = (RFC)ent.GetCustomAttributes(typeof(RFC), true)[0];

                    ci.cf = new CachedFunc();
                    ci.cf.mi = ent;

                    ret.Add(ci);
                }

                mMethodCache.Add(type, ret);
            }

            for (int b = 0, bmax = ret.Count; b < bmax; ++b)
            {
                var ci = ret[b];

                var ent = new CachedFunc();
                ent.obj = mb;
                ent.mi = ci.cf.mi;

                if (ci.rfc.id > 0)
                {
                    if (ci.rfc.id < 256) mDict0[ci.rfc.id] = ent;
                    else Console.WriteLine("RFC IDs need to be between 1 and 255 (1 byte). If you need more, just don't specify an ID and use the function's name instead.");
                    mDict1[ci.name] = ent;
                }
                else if (ci.rfc.property != null)
                {
                    //mDict1[ci.name + "/" + ci.rfc.GetUniqueID(mb)] = ent;
                }
                else mDict1[ci.name] = ent;
            }
        }

    }

    public struct CachedMethodInfo
    {
        public string name;
        public CachedFunc cf;
        public RFC rfc;
    }



    /// <summary>
    /// Remote Function Call attribute. Used to identify functions that are supposed to be executed remotely.
    /// </summary>

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class RFC : Attribute
    {
        /// <summary>
        /// Optional RFC ID, which should be in 1-254 range (inclusive). For example: [RFC(123)]. This is useful for frequent packets,
        /// as using tno.Send(123, ...) requires less bytes than tno.Send("RFC_name", ...) -- however in vast majority of the cases,
        /// it's not advisable to use IDs as it makes debugging more difficult just to save a few bytes per packet.
        /// </summary>

        public int id = 0;

        /// <summary>
        /// Name of the optional property that will be used to uniquely identify this RFC in addition to its name. This can be useful if you have
        /// multiple RFCs with an identical name underneath the same TNObject. For example, in Project 5: Sightseer, a vehicle contains multiple
        /// attachment points, with each attachment point having a "set installed item" RFC. This is done by giving all attachment points a unique
        /// identifier, ("uniqueID"), which is basically a public field set in inspector on the vehicle's prefab (but can also be a property).
        /// 
        /// RFCs then look like this:
        /// [RFC("uniqueID")] void MyRFC (...);
        /// 
        /// The syntax to send an RFC to a specific uniquely-identified child is like this:
        /// tno.Send("MyRFC/" + uniqueID, ...);
        /// </summary>

        public string property;

        public RFC(string property = null)
        {
            this.property = property;
        }

        public RFC(int rid)
        {
            id = rid;
            property = null;
        }
    }




    /// <summary>
    /// Functions gathered via reflection get cached along with their object references and expected parameter types.
    /// </summary>

    public class CachedFunc
    {
        public object obj = null;
        public MethodInfo mi;

        ParameterInfo[] mParams;
        Type[] mTypes;
        int mParamCount = 0;
        bool mAutoCast = false;

        public ParameterInfo[] parameters
        {
            get
            {
                if (mParams == null)
                {
                    if (mi == null) return null;
                    mParams = mi.GetParameters();
                    mParamCount = parameters.Length;
                }
                return mParams;
            }
        }

        /// <summary>
        /// Execute this function with the specified number of parameters.
        /// </summary>

        public object Execute(params object[] pars)
        {
            if (mi == null) return null;

            var parameters = this.parameters;
            if (pars == null && mParamCount != 0) pars = new object[parameters.Length];
            if (mParamCount == 1 && parameters[0].ParameterType == typeof(object[])) pars = new object[] { pars };

            try
            {
                return mi.Invoke(obj, pars);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);

                return null;
            }
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335