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

Ste*_*e G 2 c# properties expression-trees

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

    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();
    }
Run Code Online (Sandbox Code Playgroud)

这是工作和运作良好(谢谢StackOverflow!),但是我想通过返回T和对象的Func来删除所需的装箱/拆箱.有没有办法改变返回,以便返回类型是T的Func和typeofproperty?

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

        ... some magic happening here ...

        return (Func<T, typeofproperty>)Expression.Lambda(...more magic...).Compile();
    }
Run Code Online (Sandbox Code Playgroud)

注意 - 我正在使用VS2013和.NET 4.5

Ser*_*rvy 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();
}
Run Code Online (Sandbox Code Playgroud)

当然,这只是将问题推回给调用者.如果即使调用者不知道属性的类型是什么,那么他们也有完全相同的问题.

在一天结束时,为了避免装箱,某些代码需要在编译时知道这个属性的类型是什么.如果有人在某个地方,在编译时就会知道,那么你可以继续使用泛型来解决问题,直到你达到某个人可以使用实际已知类型的那一点.如果在编译时什么都不知道类型,那么这个问题确实没有任何解决方案.

你可能,如果你想获得技术,可以通过使事物比它们更加动态来避免装箱(使用反射来调用这些使用反射来做事物的方法)但是从技术的角度来看,你可能能够避免字面上的box命令,你会失去比你获得的更多的方式.这不是一个实际的解决方案.

  • @MichaelPerrenoud无论是将lambda的委托类型作为泛型参数提供,还是在编译后将其强制转换都不是真正相关的.它们都做同样的事情,并且代码量大致相同. (2认同)