C#之反射与特性篇

2021-03-08  本文已影响0人  wenson123

反射

定义

System.Type 类

获取Type

TestObject o = new TestObject();
Type t = o.GetType();
Type t = typeof(o);
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#常用特性

自定义特性

    /// <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;
            }
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读