The Delegate.CreateDelegate
在这种情况下不起作用,因为您必须将结果委托转换为某种已知类型,否则您所拥有的就是DynamicInvoke
这并不比直接调用更好PropertyInfo
(see here https://stackoverflow.com/questions/12858340/difference-between-invoke-and-dynamicinvoke马克·格拉维尔 (Marc Gravell) 的解释)。
我见过的最通用的方法不涉及 lambda 表达式(例如
Sriram Sakthivel 建议)由 Jon Skeet 展示here http://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/comment-page-1/。基于他的方法以及我们可以从中获取实际属性返回类型的事实PropertyInfo
,我们可以为属性调用发明一些定制的东西。
首先,我们定义一个接口:
public interface IPropertyCallAdapter<TThis>
{
object InvokeGet(TThis @this);
//add void InvokeSet(TThis @this, object value) if necessary
}
然后,该接口的实现:
public class PropertyCallAdapter<TThis, TResult> : IPropertyCallAdapter<TThis>
{
private readonly Func<TThis, TResult> _getterInvocation;
public PropertyCallAdapter(Func<TThis, TResult> getterInvocation)
{
_getterInvocation = getterInvocation;
}
public object InvokeGet(TThis @this)
{
return _getterInvocation.Invoke(@this);
}
}
The InvokeGet
方法看起来很像 Jon Skeet 使用的方法。
现在,到“神奇”的部分。我们定义一个服务,它将构建并缓存提供者的实例。它看起来像这样:
public class PropertyCallAdapterProvider<TThis>
{
private static readonly Dictionary<string, IPropertyCallAdapter<TThis>> _instances =
new Dictionary<string,IPropertyCallAdapter<TThis>>();
public static IPropertyCallAdapter<TThis> GetInstance(string forPropertyName)
{
IPropertyCallAdapter<TThis> instance;
if (!_instances.TryGetValue(forPropertyName, out instance))
{
var property = typeof(TThis).GetProperty(
forPropertyName,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo getMethod;
Delegate getterInvocation = null;
if (property != null && (getMethod = property.GetGetMethod(true)) != null)
{
var openGetterType = typeof(Func<,>);
var concreteGetterType = openGetterType
.MakeGenericType(typeof(TThis), property.PropertyType);
getterInvocation =
Delegate.CreateDelegate(concreteGetterType, null, getMethod);
}
else
{
//throw exception or create a default getterInvocation returning null
}
var openAdapterType = typeof(PropertyCallAdapter<,>);
var concreteAdapterType = openAdapterType
.MakeGenericType(typeof(TThis), property.PropertyType);
instance = Activator
.CreateInstance(concreteAdapterType, getterInvocation)
as IPropertyCallAdapter<TThis>;
_instances.Add(forPropertyName, instance);
}
return instance;
}
}
在这里,在编译时不知道确切的TResult
类型,我们创建适配器并将其缓存以供后续使用,以防止将来出现大量反射调用。
就是这样。您可以通过以下方式使用它:
PropertyCallAdapterProvider<Foo>.GetInstance("Bar").InvokeGet(fooInstance)
此外,如果需要,您可以轻松地将其扩展到属性设置器。
在我的机器上,这些是在进入循环之前从提供者预取适配器实例时使用各种方法访问循环中的 getter 一千万次的结果:
- 直接调用 141 毫秒
- 适配器调用 244 毫秒
- 反射调用 1800 毫秒
- 动态委托调用需要 8179 毫秒