非反射不转换类型地动态Property赋值、取值。

2023 年更新:

这篇文章是 2012 年写的了,至今仍然有用,可以提升需要动态调用 Property 时候的性能。不过 .NET 8 推出了一个 [UnsafeAccessor] 新特性:对于一个已知名称和类型的私有成员,通过语法糖可以在编译器级别实现直接访问内部成员了,示例如下(字段、属性、方法均可)。

 

举例,在过去,我需要访问 SemaphoreSlim 的内部成员 m_waitCount,要想高效访问,需要动用 Emit 或者先动用一次反射得到成员定义,然后再用  Delegate.CreateDelegate 编译为委托,然后调用编译后的委托实现高速访问,在 .NET8 可以简化成这样子

1
2
3
4
#if NET8
    [UnsafeAccessor(UnsafeAccessorKind.Field, Name = "m_waitCount")]
    static extern ref int GetWaitCount(SemaphoreSlim a);
#endif

 

对于外部调用属性,也类似。举例外部直接调用 class A 的私有属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Runtime.CompilerServices;
 
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "get_Value")]
static extern int GetValue(A a);
 
[UnsafeAccessor(UnsafeAccessorKind.Method, Name = "set_Value")]
static extern void SetValue(A a, int value);
 
var a = new A();
SetValue(a, 10);
Console.WriteLine(GetValue(a));
 
public class A
{
    private int Value { get; set; }
}

 

以下是原文章

-------------------------------

适用情况:

为一个不确定的对象动态地为某一个未知的Property或多个 Property 赋值和取值

亮点:

非 Property.GetValue或 Property.SetValue ,使用委托代理缓存机制。

 

因此可以这样用:

复制代码
//假设是一个一个Entity对象
var instance = new Topic();
//得到其Property Dictionary
var propDic = new InstancePropertyDictionary(instance);
//无需转换为Object地赋值
propDic.SetValue("属性名称",Int32值或Stirng值...);
//不存在类型转换地取值
Int32 int32Value = propDic.GetInt32("属性名称");
string stringValue = propDic.GetString("属性名称");
复制代码

 

 

以下是全部实现的代码,单类,可直接使用:

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Lx
{
/// <summary>
/// Instance的属性高速读/写(无需转换类型)字典
/// </summary>
class InstancePropertyDictionary
{
/// <summary>
/// Get委托
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <returns></returns>
delegate TResult Get<TResult>();

/// <summary>
/// Set委托
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <param name="value"></param>
delegate void Set<TValue>(TValue value);

/// <summary>
/// Instance
/// </summary>
object Target;

/// <summary>
/// Instance类型
/// </summary>
Type TargetType;
public InstancePropertyDictionary(object instance)
{
this.Target = instance;
this.TargetType = Target.GetType();
}

#region Set委托
/// <summary>
/// Key是属性的名字
/// Value是强类型的委托
/// </summary>
Dictionary<string, Set<Int32>> setInt32Dic = new Dictionary<string, Set<Int32>>();
Dictionary<string, Set<string>> setStringDic = new Dictionary<string, Set<string>>();
#endregion

#region Get委托
Dictionary<string, Get<Int32>> getInt32Dic = new Dictionary<string, Get<Int32>>();
Dictionary<string, Get<string>> getStringDic = new Dictionary<string, Get<string>>();
#endregion

/// <summary>
/// 装载一个类的属性
/// </summary>
public void LoadProperty(params string[] names)
{
var props = TargetType.GetProperties();
foreach (var name in names)
{
foreach (var prop in props)
{
if (prop.Name == name)
{
CreateGetSet(prop);
}
}
}
}

/// <summary>
/// 创建属性的Getter/Setter委托
/// </summary>
/// <param name="property"></param>
void CreateGetSet(PropertyInfo property)
{
string propName = property.Name;
var propType = property.PropertyType;

var propSetMethod = property.GetSetMethod();
var propGetMethod = property.GetGetMethod();
if (typeof(Int32) == propType)
{
var set = CreateSet<Int32>(propSetMethod);
setInt32Dic.Add(propName, set);

var get = CreateGet<Int32>(propGetMethod);
getInt32Dic.Add(propName, get);
}
else if (typeof(string) == propType)
{
var set = CreateSet<string>(propSetMethod);
setStringDic.Add(propName, set);

var get = CreateGet<string>(propGetMethod);
getStringDic.Add(propName, get);
}
//剩下的else if请自己实现
}


Set<T> CreateSet<T>(MethodInfo methodInfo)
{
var result = (Set<T>)Delegate.CreateDelegate(typeof(Set<T>), Target, methodInfo);
return result;
}

Get<T> CreateGet<T>(MethodInfo methodInfo)
{
var result = (Get<T>)Delegate.CreateDelegate(typeof(Get<T>), Target, methodInfo);
return result;
}

/// <summary>
/// Set值
/// </summary>
/// <param name="propertyName"></param>
/// <param name="value"></param>
public void SetValue(string propertyName, Int32 value)
{
//去字典取得强类委托型
var dg = setInt32Dic[propertyName];
dg.Invoke(value);
}

public void SetValue(string propertyName, string value)
{
var dg = setStringDic[propertyName];
dg.Invoke(value);
}

/// <summary>
/// Get值
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
public Int32 GetInt32(string propertyName)
{
var dg = getInt32Dic[propertyName];
return dg.Invoke();
}

public string GetString(string propertyName)
{
var dg = getStringDic[propertyName];
return dg.Invoke();
}

}
}
复制代码



我不知道Expression Tree是怎么使用的,是否比创建代理委托性能更好,所以贴出来,欢迎跟帖讨论。

 

附:

.NET中 Delegate.CreateDelegate方法的实现

复制代码
[SecuritySafeCritical]
public static Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (method == null)
{
throw new ArgumentNullException("method");
}
if (!(type is RuntimeType))
{
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "type");
}
RuntimeMethodInfo info = method as RuntimeMethodInfo;
if (info == null)
{
throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeMethodInfo"), "method");
}
Type baseType = type.BaseType;
if ((baseType == null) || (baseType != typeof(MulticastDelegate)))
{
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDelegate"), "type");
}
Delegate delegate2 = InternalAlloc(type.TypeHandle.GetRuntimeType());
if (delegate2.BindToMethodInfo(firstArgument, info.MethodHandle.GetMethodInfo(), info.GetDeclaringTypeInternal().TypeHandle.GetRuntimeType(), DelegateBindingFlags.RelaxedSignature))
{
return delegate2;
}
if (throwOnBindFailure)
{
throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTargMeth"));
}
return null;
}

复制代码

 

我觉得这个类中,影响性能的关键就在于  Delegate.CreateDelegate 方法的具体是怎么做的,难道还是Methodinfo.Invoke(object, object[])吗?

用Reflecter反射发现BindToMethodInfo被标记为 extern了。

 

关于反对本文所说的“非反射”的:

有人回复说,还是用到反射了。

我这里是说 不采用反射的方式去“动态Property赋值、取值”,本文标题也没有误导吧?

CreateSet<T>
CreateGet<T>  

这两个方法的实现可以知道,已经不是在用反射了,而是委托


前面用到了反射的地方,只是去取得Property的GetMethod和SetMethod,然后用来创建强类型的委托。

所以在给Property赋值的时候,是不存在反射的。
而且可以看 Secmoo回复 的测试结果,如果在取值、赋值过程中涉及到了反射机制,是决不会有能超过Expression的性能的。
 

posted @   darklx  阅读(2430)  评论(13编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示