3

コンパイル時にオブジェクトタイプを知らなくても、オブジェクトにプロパティ値を設定したい。高速にしたい(つまり、毎回Reflectionを使用しない)。プロパティの名前とタイプを知っています。

最速の方法(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");

ただし、Objectの代わりにUserタイプのobjectを渡すという事実は気に入らず、「Type'User'のParameterExpressionはタイプ'System.Objectのデリゲートパラメータには使用できません」で失敗します。

私は式ツリーに慣れていないので、ここで少し迷子になりました。ユーザーをオブジェクトにキャストできないのはなぜですか?どこかにキャストが必要ですか?

「アクション」も見栄えがよくありません。引数(User user、string propertyValue)をとるデリゲートを返すだけの方が良いでしょう。繰り返しますが、それを達成する方法がわかりません。実際、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 に答える