【问题标题】:Setting property without knowing target type at compile time在编译时不知道目标类型的情况下设置属性
【发布时间】:2012-05-25 19:12:23
【问题描述】:

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

最快的方法(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' 类型的 ParameterExpression 不能用于'System.Object'类型的委托参数”。

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

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

有什么想法吗?

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

【问题讨论】:

  • TL;DR 不要这样做。用相关信息和细节写出简洁明了的问题。
  • 已编辑。虽然这是对整个问题的总结,并且有足够的细节来提供答案,imo。
  • 这有点偏题了,但感觉就像你是一个习惯使用模板但现在不能在 C# 中使用的 C++ 程序员。这在 C++ 中是微不足道的。
  • 这可能就是你要找的Convert.ChangeType Method (Object, Type)
  • User 是值类型还是引用类型?

标签: c# c#-4.0


【解决方案1】:

如果你想使用表达式,那么: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();
}

【讨论】:

  • 这太棒了!:) 谢谢!我有一些与您的第二个示例非常相似的内容,但是正在努力使其更通用。我想我知道我做错了什么。感谢 FastMember 的提示,我一定会看看。
  • 太棒了,整天都在寻找这样的东西。我能找到的只是带有类型代表的项目!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-16
  • 1970-01-01
相关资源
最近更新 更多