C#动态获取对象属性值,这才是高效正确的姿势!

共 3975字,需浏览 8分钟

 ·

2021-01-11 20:20


转自:ZKEASOFT
zkea.net/codesnippet/detail/csharp-fast-get-property-value.html

动态获取对象的性能值,这个在开发过程中经常会遇到,这里我们探讨一下如何高性能的获取属性值。为了对比测试,我们定义一个类People


public class People
{
public string Name { get; set; }
}


然后通过直接代码调用方式来取1千万次看要花多少时间:


private static void Directly()
{
People people = new People { Name = "Wayne" };
Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
object value = people.Name;
}
stopwatch.Stop();
Console.WriteLine("Directly: {0}ms", stopwatch.ElapsedMilliseconds);
}


大概花了37ms:



反射


通过反射来获取对象的属性值,这应该是大家常用的方式,但这种方式的性能比较差。接下来我们来看看同样取1千万次需要多少时间:


private static void Reflection()
{
People people = new People { Name = "Wayne" };
Type type = typeof(People);
PropertyInfo property = type.GetProperty("Name");
Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
object value = property.GetValue(people);
}
stopwatch.Stop();
Console.WriteLine("Reflection: {0}ms", stopwatch.ElapsedMilliseconds);
}


大概花了1533ms,果然要慢很多:



那既然反射慢,那还有没有其它方式呢?


动态构建Lambda


我们知道可以动态构建Linq的Lambda表达式,然后通过编译后得到一个委托,如果能动态构建返回属性值的委托,就可以取到值了。所以我们想办法构建一个像这样的委托:


Func getName = m => m.Name;


接下来我们就通过Expression来构建:


private static void Lambda()
{
People people = new People { Name = "Wayne" };
Type type = typeof(People);
var parameter = Expression.Parameter(type, "m");//参数m
PropertyInfo property = type.GetProperty("Name");
Expression expProperty = Expression.Property(parameter, property.Name);//取参数的属性m.Name
var propertyDelegateExpression = Expression.Lambda(expProperty, parameter);//变成表达式 m => m.Name
var propertyDelegate = (Funcobject
>)propertyDelegateExpression.Compile();//编译成委托
Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
object value = propertyDelegate.Invoke(people);
}
stopwatch.Stop();
Console.WriteLine("Lambda:{0}ms", stopwatch.ElapsedMilliseconds);
}


然后我们测试一下,大概花了138ms,性能要比反射好非常多:



委托调用


虽然动态构建Lambda的性能已经很好了,但还是更好吗?毕竟比直接调用还是差了一些,要是能直接调用属性的取值方法就好了。


在C#中,可读属性都有一个对应的get_XXX()的方法,可以通过调用这个方法来取得对应属性的值。可以使用System.Delegate.CreateDelegate创建一个委托来调用这个方法。


  • 通过委托调用方法来取得属性值


我们定义一个MemberGetDelegate的委托,然后通过它来调用取值方法:


delegate object MemberGetDelegate(People p);
private static void Delegate()
{
People people = new People { Name = "Wayne" };
Type type = typeof(People);
PropertyInfo property = type.GetProperty("Name");
MemberGetDelegate memberGet = (MemberGetDelegate)System.Delegate.CreateDelegate(typeof(MemberGetDelegate), property.GetGetMethod());
Stopwatch stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 10000000; i++)
{
object value = memberGet(people);
}
stopwatch.Stop();
Console.WriteLine("Delegate: {0}ms", stopwatch.ElapsedMilliseconds);
}


然后我们测试一下,大概花了38ms,性能几乎与直接调用一致:



最后做一个简单的封装,缓存一下创建的Delegate


public class PropertyValue
{
private static ConcurrentDictionary<string, MemberGetDelegate> _memberGetDelegate = new ConcurrentDictionary<string, MemberGetDelegate>();
delegate object MemberGetDelegate(T obj);
public PropertyValue(T obj)
{
Target = obj;
}
public T Target { get; private set; }
public object Get(string name)
{
MemberGetDelegate memberGet = _memberGetDelegate.GetOrAdd(name, BuildDelegate);
return memberGet(Target);
}
private MemberGetDelegate BuildDelegate(string name)
{
Type type = typeof(T);
PropertyInfo property = type.GetProperty(name);
return (MemberGetDelegate)Delegate.CreateDelegate(typeof(MemberGetDelegate), property.GetGetMethod());
}
}


这样使用起来就方便多了


People people = new People { Name = "Wayne" };
PropertyValue propertyValue = new PropertyValue(people);
object value = propertyValue.Get("Name");


- EOF -

回复 【关闭】广
回复 【实战】获取20套实战源码
回复 【被删】
回复 【访客】访
回复 【小程序】学获取15套【入门+实战+赚钱】小程序源码
回复 【python】学微获取全套0基础Python知识手册
回复 【2019】获取2019 .NET 开发者峰会资料PPT
回复 【加群】加入dotnet微信交流群

副业刚需,没有人能拒绝这个网站!


终于GitHub App 已支持简体中文!


浏览 28
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报