3

我想在编译时不知道对象类型的情况下设置对象的属性值;我希望它快(即不是每次都使用反射);我知道属性名称和类型。

最快的方法(afaik)是使用代表;所以这就是我到目前为止所拥有的:

class User // this is an example.. Assume I don't know which type this is.
 {
    public string Name {get;set;}   
 }

public static Action<object, object> CreatePropertySetter(Type targetType, string propertyName)
{
    ParameterExpression targetObjParamExpr = Expression.Parameter(targetType);
    ParameterExpression valueParamExpr = Expression.Parameter(targetType.GetProperty(propertyName).PropertyType);

    MemberExpression propertyExpr = Expression.Property(targetObjParamExpr, propertyName);

    BinaryExpression assignExpr = Expression.Assign(targetObjParamExpr, valueParamExpr);

    Action<object, object> result = Expression.Lambda<Action<object, object>>(assignExpr, targetObjParamExpr, valueParamExpr).Compile();
    return result;
}

然后我会打这个电话:

User user = new User();
var userNameSetter = CreatePropertySetter(user.GetType(), "Name");
userNameSetter(user, "Bob");

但是,它不喜欢我传递 User 类型对象而不是 Object 的事实,并且由于“ 'User' 类型的 ParameterExpression 不能用于'System.Object'类型的委托参数”而失败。

我是表达式树的新手,所以在这里有点迷路。为什么它不能将 User 转换为 object ?我需要在某个地方进行演员表吗?

“动作”看起来也不太好;只返回一个带参数的委托(用户用户,字符串属性值)会更好。同样,不知道如何做到这一点。实际上,我已经用 Delegate.CreateDelegate 尝试过,但它使用 .Invoke() 方法调用,这很慢(这是唯一的方法吗?);与 Expression.Lambda 相同(非泛型)。

有什么想法吗 ?

此外,是否有关于表达式树的好的(比 msdn 更好的)文档?msdn 版本确实缺乏细节。

4

1 回答 1

9

如果要使用表达式,则:Convert...

static void Main()
{
    var setter = CreatePropertySetter(typeof (User), "Name");
    var obj = new User();
    setter(obj, "Fred");
}
public static Action<object, object> CreatePropertySetter(Type targetType, string propertyName)
{
    var target = Expression.Parameter(typeof (object), "obj");
    var value = Expression.Parameter(typeof (object), "value");
    var property = targetType.GetProperty(propertyName);
    var body = Expression.Assign(
        Expression.Property(Expression.Convert(target, property.DeclaringType), property),
        Expression.Convert(value, property.PropertyType));

    var lambda = Expression.Lambda<Action<object, object>>(body, target, value);
    return lambda.Compile();
}

然而!您可能想查看FastMember(也可在 NuGet 上获得),它非常方便地为您包装了所有这些(并使用原始 IL 来进行愚蠢的疯狂操作)。

如果要使用类型化委托,则需要提前知道类型。如果你知道类型,你可以添加一些通用的:

static void Main()
{
    var setter = CreatePropertySetter<User,string>("Name");
    var obj = new User();
    setter(obj, "Fred");
}
public static Action<TType, TValue> CreatePropertySetter<TType, TValue>(string propertyName)
{
    var target = Expression.Parameter(typeof (TType), "obj");
    var value = Expression.Parameter(typeof (TValue), "value");
    var property = typeof(TType).GetProperty(propertyName);
    var body = Expression.Assign(
        Expression.Property(target, property),
        value);

    var lambda = Expression.Lambda<Action<TType, TValue>>(body, target, value);
    return lambda.Compile();
}
于 2012-05-25T19:21:54.293 回答