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

时间:2014-05-07 18:06:17

标签: 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();
    }

这工作正常并且运行良好(谢谢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();
    }

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

2 个答案:

答案 0 :(得分: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();
}

当然,这只是将问题推回给调用者。如果连呼叫者都不知道该属性的类型,那么他们就会遇到完全相同的问题。

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

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

答案 1 :(得分:0)

上面关于再次使用反射的评论让我思考(总是一个危险的前景),我提出了以下解决方案,基本上实现了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();
        }
    }

这项工作,提供类型安全的访问,并避免任何明显的装箱/拆箱。由于这是在我的程序开始时发生的一次(并且仅针对实际访问的属性),我可以使用反射的性能命中,以换取属性访问器的类型代理。