C#之反射與特性篇

反射

定義

  • 類型 Type:描述程序集棍丐,類型等的元數(shù)據(jù)信息
  • 反射 Reflect:
    一個運行時獲取元數(shù)據(jù)信息的過程,得到一個給定程序集所包含的所有類型的列表丽蝎,包括給定類型的方法斗塘,字段,屬性和事件擎鸠,接口,方法的參數(shù)和其他相關(guān)細節(jié)(基類缘圈,命名空間劣光,清單數(shù)據(jù))

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");

查看元數(shù)據(jù)

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>
        /// 反射方法 參數(shù) 和 返回值
        /// </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 = "( ";
                //得到參數(shù)
                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 動態(tài)加載程序集,反射獲取程序集的元數(shù)據(jù)

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)

定義

創(chuàng)建一個給定類型的實例绢涡,并在運行時調(diào)用其方法,而不需要在編譯期知道它存在的一種技術(shù)

方法

System.Activator.CreateInstance() 創(chuàng)建運行時實例

MethodInfo.Invoke() 執(zhí)行實例的方法

    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);

                //調(diào)用無參數(shù)函數(shù)
                MethodInfo hello = myClass.GetMethod("hello");
                Console.WriteLine("call {0} ", hello.Invoke(obj, null));

                //調(diào)用包含參數(shù)的函數(shù)
                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:限定應(yīng)用范圍遣疯; 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 反射和特性的應(yīng)用

  1. [RFC] 定義遠程函數(shù)調(diào)用特性
  2. CachedFunc 封裝MethodInfo.Invoke函數(shù)執(zhí)行方法
  3. BuildMethodList 發(fā)射獲取RFC 函數(shù)并保存到本地列表中
  4. 從本地列表中查詢RFC函數(shù)執(zhí)行
/// <summary>
/// TNet 反射 特性應(yīng)用
/// 用RFC 特性
/// 利用反射機制 保存具有RFC的obj 和 函數(shù)
/// 然后網(wǎng)絡(luò)層收到消息后執(zhí)行函數(shù)
/// </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;
            }
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末垂寥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子另锋,更是在濱河造成了極大的恐慌滞项,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夭坪,死亡現(xiàn)場離奇詭異文判,居然都是意外死亡,警方通過查閱死者的電腦和手機室梅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門戏仓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亡鼠,你說我怎么就攤上這事赏殃。” “怎么了间涵?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵仁热,是天一觀的道長。 經(jīng)常有香客問我勾哩,道長抗蠢,這世上最難降的妖魔是什么举哟? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮迅矛,結(jié)果婚禮上妨猩,老公的妹妹穿的比我還像新娘。我一直安慰自己秽褒,他們只是感情好壶硅,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著销斟,像睡著了一般森瘪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上票堵,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機與錄音逮栅,去河邊找鬼悴势。 笑死,一個胖子當(dāng)著我的面吹牛措伐,可吹牛的內(nèi)容都是我干的特纤。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼侥加,長吁一口氣:“原來是場噩夢啊……” “哼捧存!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起担败,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤昔穴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后提前,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吗货,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年狈网,在試婚紗的時候發(fā)現(xiàn)自己被綠了宙搬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡拓哺,死狀恐怖勇垛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情士鸥,我是刑警寧澤闲孤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站烤礁,受9級特大地震影響崭放,放射性物質(zhì)發(fā)生泄漏哨苛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一币砂、第九天 我趴在偏房一處隱蔽的房頂上張望建峭。 院中可真熱鬧,春花似錦决摧、人聲如沸亿蒸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽边锁。三九已至,卻和暖如春波岛,著一層夾襖步出監(jiān)牢的瞬間茅坛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工则拷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贡蓖,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓煌茬,卻偏偏與公主長得像斥铺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坛善,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351