C# 创建用于缓存属性访问器的委托

C# 创建用于缓存属性访问器的委托,c#,properties,expression-trees,C#,Properties,Expression Trees,我正在使用以下代码创建和缓存属性访问器委托: static Delegate CreateGetterDelegate<T>(PropertyInfo propertyInfo) { if (typeof(T) != propertyInfo.DeclaringType) { throw new ArgumentException(); } var instance = Expr

我正在使用以下代码创建和缓存属性访问器委托:

    static Delegate CreateGetterDelegate<T>(PropertyInfo propertyInfo)
    {
        if (typeof(T) != propertyInfo.DeclaringType)
        {
            throw new ArgumentException();
        }

        var instance = Expression.Parameter(propertyInfo.DeclaringType);
        var property = Expression.Property(instance, propertyInfo);
        var convert = Expression.TypeAs(property, typeof(object));
        return (Func<T, object>)Expression.Lambda(convert, instance).Compile();
    }
静态委托CreateGetterDelegate(PropertyInfo PropertyInfo)
{
if(类型(T)!=属性信息去极化类型)
{
抛出新ArgumentException();
}
var instance=Expression.Parameter(propertyInfo.DeclaringType);
var property=Expression.property(实例,propertyInfo);
var convert=Expression.TypeAs(属性,typeof(对象));
return(Func)Expression.Lambda(convert,instance.Compile();
}
这是工作和工作良好(谢谢StackOverflow!),但我想删除的装箱/拆箱所需的返回函数的T和对象。有没有办法更改返回,使返回类型为T和typeofproperty的Func

静态委托CreateGetterDelegate(PropertyInfo PropertyInfo)
{
if(类型(T)!=属性信息去极化类型)
{
抛出新ArgumentException();
}
…这里发生了一些神奇的事情。。。
返回(Func)表达式.Lambda(…更神奇…).Compile();
}

注意-我使用的是VS2013和.NET 4.5,唯一的方法是在编译时知道属性的类型,无论是在这个方法定义中还是调用方。如果此方法的调用方知道属性的类型,则可以使用第二个泛型参数指定它:

static Func<TInstance, TResult> CreateGetterDelegate<TInstance, TResult>(
    PropertyInfo propertyInfo)
{
    if (typeof(TInstance) != propertyInfo.DeclaringType)
    {
        throw new ArgumentException();
    }

    var instance = Expression.Parameter(propertyInfo.DeclaringType);
    var property = Expression.Property(instance, propertyInfo);
    var convert = Expression.TypeAs(property, typeof(object));
    return (Func<TInstance, TResult>)Expression.Lambda(convert, instance)
        .Compile();
}
static Func CreateGetterDelegate(
PropertyInfo PropertyInfo)
{
if(typeof(TInstance)!=propertyInfo.DeclaringType)
{
抛出新ArgumentException();
}
var实例=表达式参数(propertyInfo.DeclaringType);
var property=Expression.property(实例,propertyInfo);
var convert=Expression.TypeAs(属性,typeof(对象));
return(Func)Expression.Lambda(convert,instance)
.Compile();
}
当然,这只是将问题推回给调用者。如果连调用者都不知道属性的类型,那么他们就有完全相同的问题

最后,为了避免装箱,某些代码需要在编译时知道此属性的类型。如果某个地方的某个人在编译时就知道了,那么您可以继续使用泛型来解决问题,直到有人可以使用实际的已知类型为止。如果在编译时没有人知道类型,那么这个问题就没有任何解决方案


如果您想获得技术性,可以通过使事情变得更加动态来避免装箱(使用反射来调用这些使用反射的方法),但是从技术角度来看,虽然您可以避免使用literal
box
命令,但您将失去比您获得的更多的东西。这不是一个实际的解决方案。

上面关于使用反射的评论再次让我思考(总是一个危险的前景),我提出了以下解决方案,基本上实现了Servy的建议:

    static Delegate CreateGenericGetterDelegate<TClass>(PropertyInfo propertyInfo)
    {
        var method = typeof(Cache).GetMethod("CreateTypedGetterDelegate"); 
        MethodInfo generic = method.MakeGenericMethod(new Type[] { typeof(TClass), propertyInfo.PropertyType });
        return (Delegate) generic.Invoke(new object(), new object[] { propertyInfo });
    }

    public static Delegate CreateTypedGetterDelegate<TClass, TProp>(PropertyInfo propertyInfo)
    {
        if (typeof(TClass) != propertyInfo.DeclaringType)
        {
            throw new ArgumentException();
        }

        var instance = Expression.Parameter(propertyInfo.DeclaringType);
        var property = Expression.Property(instance, propertyInfo);
        if (typeof(TProp).IsValueType)
        {
            return (Func<TClass, TProp>)Expression.Lambda(property, instance).Compile();
        }
        else
        {
            return (Func<TClass, TProp>)Expression.Lambda(Expression.TypeAs(property, typeof(TProp)), instance).Compile();
        }
    }
静态委托CreateGenericGetterDelegate(PropertyInfo PropertyInfo)
{
var method=typeof(Cache).GetMethod(“CreateTypedGetterDelegate”);
MethodInfo generic=method.MakeGenericMethod(新类型[]{typeof(TClass),propertyInfo.PropertyType});
return(Delegate)generic.Invoke(新对象(),新对象[]{propertyInfo});
}
公共静态委托CreateTypedGetterDelegate(PropertyInfo PropertyInfo)
{
if(typeof(TClass)!=propertyInfo.DeclaringType)
{
抛出新ArgumentException();
}
var instance=Expression.Parameter(propertyInfo.DeclaringType);
var property=Expression.property(实例,propertyInfo);
if(typeof(TProp).IsValueType)
{
return(Func)Expression.Lambda(属性,实例).Compile();
}
其他的
{
return(Func)Expression.Lambda(Expression.TypeAs(property,typeof(TProp)),instance.Compile();
}
}

这可以工作,提供类型安全访问,并避免任何明显的装箱/拆箱。由于这种情况在我的程序开始时发生过一次(并且仅针对实际访问的属性),因此我对反射的性能命中表示满意,作为对属性访问器的类型化委托的回报。

这种装箱/拆箱方式如何?你只是在施放一个ref类型..@TMcKeown,如果
T
int
的话,那就是装箱了。好的,明白了。您必须能够更改
委托
+1;我相信你也可以做
Expression.Lambda
对吗?但是我想你还是得把它转换成一个
Func
huh?@MichaelPerrenoud不管你是提供lambda的委托类型作为它的泛型参数,还是在编译后转换它都不是很相关。它们都做相同的事情,并且代码量大致相同。这种方法没有任何用途。给定的委托仍然需要强制转换为实际的委托类型才能调用它,这需要在编译时知道属性的类型。要么如此,要么需要使用
DynamicInvoke
,这仍然会对结果进行装箱。您的
CreateGenericGetterDelegate
方法在OP中的实现上增加了零值,因为您仍然返回一个
委托,而不是从它派生的东西。嗯。。。所以,基本上你说的是,虽然这在技术上是正确的(也就是说,它是有效的),但我真正做的是大量的工作,因为返回一个代理并对结果使用DynamicVoke,所以没有任何实际收益?所以,至少我学到了一些东西,即使我做了类似于以前的练习,每个人都在红绿灯处下车,绕着车跑,试图在灯亮之前回到车里
    static Delegate CreateGenericGetterDelegate<TClass>(PropertyInfo propertyInfo)
    {
        var method = typeof(Cache).GetMethod("CreateTypedGetterDelegate"); 
        MethodInfo generic = method.MakeGenericMethod(new Type[] { typeof(TClass), propertyInfo.PropertyType });
        return (Delegate) generic.Invoke(new object(), new object[] { propertyInfo });
    }

    public static Delegate CreateTypedGetterDelegate<TClass, TProp>(PropertyInfo propertyInfo)
    {
        if (typeof(TClass) != propertyInfo.DeclaringType)
        {
            throw new ArgumentException();
        }

        var instance = Expression.Parameter(propertyInfo.DeclaringType);
        var property = Expression.Property(instance, propertyInfo);
        if (typeof(TProp).IsValueType)
        {
            return (Func<TClass, TProp>)Expression.Lambda(property, instance).Compile();
        }
        else
        {
            return (Func<TClass, TProp>)Expression.Lambda(Expression.TypeAs(property, typeof(TProp)), instance).Compile();
        }
    }