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