在编译时设置属性而不知道目标类型

时间:2012-05-25 19:12:23

标签: c# c#-4.0

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

最快的方式(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转换为对象?我需要在某个地方演员吗?

“行动”看起来也不好看;只返回一个带参数的委托(User user,string propertyValue)会更好。再次,不知道如何实现这一目标。实际上,我已经尝试过Delegate.CreateDelegate,但它使用.Invoke()方法调用,这很慢(这是唯一的方法吗?);与Expression.Lambda(非泛型)相同。

有什么想法吗?

此外,表达式树上有一个好的(比msdn好)文档吗? msdn版本确实缺乏细节。

1 个答案:

答案 0 :(得分:9)

如果您想使用Expression,那么: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();
}