LINQ TO Reflection(反射)

引言

我们平时开发中不可避免,或者说,经常性的使用反射。但是却没有一个合适的类库帮助我们更好的利用反射。从早期的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

 

posted @ 2010-08-11 01:08 浪子 阅读(2831) 评论(27) 编辑 收藏

 回复 引用 查看   
#1楼 2010-08-11 08:06 MicroCoder      
这个不错~~~
 回复 引用 查看   
#2楼 2010-08-11 08:37 ocean      
原来还是熟人
 回复 引用 查看   
#3楼 2010-08-11 08:38 Steven Chen      
只有把大量的反射操作恰当的应用到具体的业务场景中,这个库才能发挥作用,如果项目中使用反射的操作很少,用这个就有些牛刀了........
 回复 引用 查看   
#4楼 2010-08-11 08:45 温景良(Jason)      
呵呵,俺也是厦门的
 回复 引用 查看   
#5楼 2010-08-11 09:10 allentranks      
俺也是厦门的~
 回复 引用 查看   
#6楼 2010-08-11 09:12 鹤冲天      
好文,支持!
 回复 引用 查看   
#7楼 2010-08-11 09:21 追风      
还在学习中,偶也是厦门的
 回复 引用 查看   
#8楼 2010-08-11 10:01 坤坤      
呵呵,可以开一个厦门报道贴了。^^
 回复 引用 查看   
#9楼 2010-08-11 10:58 netguid      
property.SetValue(obj, changedLocation);赋值一直没有成功
 回复 引用 查看   
#10楼 2010-08-11 11:24 陈梓瀚(vczh)      
为啥不吧proxy的功能融合进metadata,这样我就根本不需要考虑我想metadata还是proxy了,直接就好了……

还有那个dynamic的问题,其实那个已经足够动态了。因为dynamic不会跟模板特化一样替你生成很多份代码,里面也是用的反射。

 回复 引用 查看   
#11楼 2010-08-11 11:28 ryanding      
我也是厦门的,而且也是熟人!
 回复 引用 查看   
#12楼[楼主] 2010-08-11 12:17 浪子      
@MicroCoder
3x

@ocean
@ryanding
都是熟人

@温景良(Jason)
@allentranks
@追风
@坤坤
有机会聚下,就成熟人了^_^

@Steven Chen
只要需要反射都可以用,这个只是个小类库而已。

@netguid
为什么说没有成功? 你仔细检查下。 Test跑过都没有问题。建议下载完整的example代码看看。

@陈梓瀚(vczh)
两个有显著的差别。
.Metadata() 只依赖于于Type类型,是全局缓存的
.Proxy() 不仅依赖于Type类型,还依赖于它的实例,不能被全局缓存,也没有必要(不然可能导致内存泄漏)

 回复 引用 查看   
#13楼 2010-08-11 12:43 求索者      
请问是否有与其他类库在性能上的对比数据?
 回复 引用 查看   
#14楼[楼主] 2010-08-11 12:47 浪子      
@求索者
理论上和http://fastreflectionlib.codeplex.com/的一样。差别在缓存的命中速率上,这里使用并发集合,以便支持多线程应用,可能会多损耗一些性能,不过相差应该不会太多。
我自己没有做详细的测试比对,你可以看下老赵的测试数据
http://fastreflectionlib.codeplex.com/wikipage?title=Benchmarks&referringTitle=Home

 回复 引用 查看   
#15楼 2010-08-11 12:52 Timothy      
这么晚还在奋斗,,
 回复 引用 查看   
#16楼 2010-08-11 13:04 喵 喵      
这样的反射 不错
 回复 引用 查看   
#17楼 2010-08-11 14:07 Dozer      
Linq V5
 回复 引用 查看   
#18楼 2010-08-11 14:16 airwolf2026      
获取自定义属性的方式感觉还是太繁了一点哈...(可能和好久没有写linq有关)
 回复 引用 查看   
#19楼[楼主] 2010-08-11 14:28 浪子      
@airwolf2026
我的那个事例是跨多个Property取同一类型Attribute(SelectMany),如果你只对一个Property取Attribute会很简单的。大概像下面的代码
var attribute = obj.Metadata().Properties
                .Where(i => i.Name == "Name")
                .FirstOrDefault()
                .Attributes
                .OfType<MockAttribute>()
                .FirstOrDefault();



 回复 引用 查看   
#20楼[楼主] 2010-08-11 15:57 浪子      
只是使用上面的写法在FirstOrDefault()之后的.Attributes可能会抛出NullReferenceException.

而使用SelectMany的投影写法则没有这个问题。

 回复 引用 查看   
#21楼 2010-08-11 22:06 南院那      
这么多厦门的
 回复 引用 查看   
#22楼 2010-08-11 22:47 airwolf2026      
上面的例子就简单了哈.可能俺笨的原因哈.

ps:俺觉得厦门适合养老哈,怀念白城海滩和珍珠湾啊.还有那附近的环岛路,记得那天夏天和同学一起穿大裤衩骑破自行车然去游泳...

 回复 引用 查看   
#23楼 2010-08-11 23:24 ppchen(陈荣林)      
熟人,厦门报道贴哈哈
 回复 引用 查看   
#24楼[楼主] 2010-08-12 06:40 浪子      
@airwolf2026
哈哈,那可以等老了回来养。

 回复 引用 查看   
#25楼 2010-08-13 10:31 淡淡的云      
我也是厦门的~~~
 回复 引用 查看   
#26楼[楼主] 2010-09-15 22:56 浪子      
@求索者
已经做了性能测试比较,请查看的Benchmarks
http://sparrow.codeplex.com/wikipage?title=LINQ-To-Reflection&referringTitle=Documentation

 回复 引用 查看   
#27楼 2010-12-23 09:53 欧阳寒玟      
厦门如果聚会别忘了通知我,熟人们,我们是最熟悉的陌生人。
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1797024 cVyAnydMm0Q=