LINQ TO Reflection

作者: 浪子  來源: 博客園  發布時間: 2010-08-19 11:16  閱讀: 1126 次  推薦: 0   原文鏈接   [收藏]  
摘要:本文將討論的是LINQ的反射機制。需要解決的是反射性能損耗問題,其次就是易用性方面的改進,希望對大家有所幫助。

  引言

  我們平時開發中不可避免,或者說,經常性的使用反射。但是卻沒有一個合適的類庫幫助我們更好的利用反射。從早期的FastInvoker,到老趙的 fastreflectionlib ,無一不是在強調Fast。這是因為反射的性能損耗比較厲害,所以大家都集中精力解決性能的問題,但是在易用性方面卻鮮有改進。今天我為大家帶來一個即兼顧性能又具有良好的使用體驗的反射類庫.

  .Metadata()

  此類庫以LINQ TO Object為基礎,可以通過調用.Metadata()方法獲取對應Type的完整Metadata信息。此信息會被進行緩存,并且使用fastreflectionlib的核心Lambda代碼,利用DynamicMethod代替直接的反射執行。

        public static Metadata Metadata(this object instance)
        {
            return MetadataCache.Create(instance);
        }

     先定義一個MockObject

        class MockAttribute : Attribute
        {
            public MockAttribute(string name)
            {
                this.Name = name;
            }
            public string Name
            {
                get;set;
            }
        }

        class MockObject
        {
            public string Country = "China";
            
            [Mock("this is the name")]
            public string Name
            {
                get;set;
            }
            public string Blog
            {
                get;set;
            }
            [Mock("this is the location")]
            public string Location
            {
                get;set;
            }
            public string SayHello(string name)
            {
                return "Hi," + name;
            }
        }
    }

   1.如何獲取一個屬性,并進行取值、賦值?

        using Sparrow.Reflection;
        [TestMethod]
        public void set_property_value()
        {
            var obj = new MockObject { Name = "dayi", Blog = "http://walkingboy.cnblogs.com", Location = "XiaMen" };
            var property = obj.Metadata().Properties.Where(i => i.Name == "Location").FirstOrDefault();
            var changedLocation = "Xiamen,China";
            //get value
            //var value = property.GetValue(obj);
            property.SetValue(obj, changedLocation);
            Assert.AreEqual(changedLocation, obj.Location);
        }

   2.如果獲取一個字段的值?

        using Sparrow.Reflection;
        [TestMethod]
        public void get_field_value()
        {
            var obj = new MockObject();

            var field = obj.Metadata().Fields.Where(i => i.Name == "Country").FirstOrDefault();

            Assert.AreEqual("China", field.GetValue(obj));
        }

   3.如何獲取一個自定義CustomAttribute?

        using Sparrow.Reflection;
        [TestMethod]
        public void get_custom_attribute_data()
        {
            var obj = new MockObject { Name = "dayi", Blog = "http://walkingboy.cnblogs.com", Location = "XiaMen" };


            var attribute = obj.Metadata().Properties
                .Where(i => i.Name == "Name")
                .SelectMany(i => i.Attributes)
                .Select(i=>i.Attribute)
                .OfType<MockAttribute>()
                .FirstOrDefault();

            Assert.AreEqual("this is the name", attribute.Name);
        }

   4.如何調用一個指定名稱的Method?

        using Sparrow.Reflection;
        [TestMethod]
        public void invoke_method()
        {
            var obj = new MockObject();

            var method = obj.Metadata().Methods.Where(i => i.Name == "SayHello").FirstOrDefault();

            Assert.AreEqual("Hi,world",method.Invoke(obj,new []{"world"}));
        }

  .Proxy()

  對于某些應用場景來說,使用LINQ To Object去查詢并獲取單一的方法、屬性,字段,總覺得還是要寫非常多的代碼。要先.Metadata(), 接下來.Where(), 雖然代碼很優雅,但是還是有很多工作要做。所以這里也提供一個針對獲取單一方法、屬性、字段的替代寫法。

        public static Proxy Proxy(this object instance)
        {
            return new Proxy(instance);
        }

  1.如何獲取一個屬性的值

        using Sparrow.Reflection;
        [TestMethod]
        public void get_value_via_property_proxy()
        {
            var obj = new MockObject { Name = "dayi", Blog = "http://walkingboy.cnblogs.com", Location = "Xiamen" };

            Assert.AreEqual(obj.Name, obj.Proxy().Properties["Name"]);
        }

  2.如何設置一個屬性的值

        using Sparrow.Reflection;
        [TestMethod]
        public void set_value_via_property_proxy()
        {
            var obj = new MockObject { Name = "dayi", Blog = "http://walkingboy.cnblogs.com", Location = "Xiamen" };

            var changedLocation = "Xiamen,China";
            obj.Proxy().Properties["Location"] = changedLocation;

            Assert.AreEqual(changedLocation,obj.Location);
        }
 
 

  3.如何獲取一個字段的值

        using Sparrow.Reflection;
        [TestMethod]
        public void get_value_via_field_proxy()
        {
            var obj = new MockObject { Name = "dayi", Blog = "http://walkingboy.cnblogs.com", Location = "Xiamen" };

            Assert.AreEqual(obj.Country, obj.Proxy().Fields["Country"]);
        }

  4.如何調用一個方法

        using Sparrow.Reflection;
        [TestMethod]
        public void invoke_method_via_method_proxy()
        {
            var obj = new MockObject();

            Assert.AreEqual("Hi,world", obj.Proxy().Methods["SayHello"](new []{"world"}));
        }

  .Proxy() Vs Dynamic

  我們知道在C# 4中引入了關鍵字Dynamic,使得 Duck Typing (DynamicDuck: Duck Typing in a Dynamic World) 成為一種可能。 查看如下代碼

        public void Run(dynamic obj)
        {  
            Console.WriteLine(obj.Name);
        }

  這個代碼并沒有指定參數obj的類型,obj的類型是由運行時候傳入的真實值決定,只要該類型包含一個Name的屬性就可以了。

  但是僅僅支持Duck Typing就夠了嘛? 似乎不夠動態哦。這邊的.Name 是在編譯時(或者說編碼時)就確定,但是往往我們的使用場景中,連這個也都是動態,比如是接受自Form,或者來自配置信息,這個時候dynamic就無能為力了。反過來看看使用.Proxy()的情況

       public void Run(object obj,string propertyName)
       { 
           Console.WriteLine(obj.Proxy().Properties[propertyName])
       }

  不僅支持Duck Typing,同時也支持屬性名稱的動態化,是不是很有腳本(javascript…)的感覺呢?

  代碼下載http://sparrow.codeplex.com/releases/view/50364

  詳細文檔http://sparrow.codeplex.com/wikipage?title=LINQ-To-Reflection

0
0
 
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()