18

私は次のクラスを持っています

public class MyClass
{
    public bool Delete(Product product)
    {
        // some code.
    }
}

今、私はこのようなヘルパークラスを持っています

public class Helper<T, TResult>
{

    public Type Type;
    public string Method;
    public Type[] ArgTypes;
    public object[] ArgValues;

    public Helper(Expression<Func<T, TResult>> expression)
    {
        var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;

        this.Type = typeof(T);
        this.Method = body.Method.Name;
        this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray();
        this.ArgValues = ???
    }
}

アイデアは、どこかからこのコードを使用することです:

// I am returning a helper somewhere
public Helper<T> GetMethod<T>()
{
    var product = GetProduct(1);
    return new Helper<MyClass>(x => x.Delete(product));
}

// some other class decides, when to execute the helper 
// Invoker already exists and is responsible for executing the method
// that is the main reason I don't just comile and execute my Expression
public bool ExecuteMethod<T>(Helper<T> helper)
{
    var instance = new MyClass();
    var Invoker = new Invoker(helper.Type, helper.Method, helper.ArgTypes, helper.ArgValues);
    return (bool)Invoker.Invoke(instance);
}

私が立ち往生している点は、式自体から引数を抽出する方法です。

私はこの方法を見つけました

((ConstantExpression)((MemberExpression)body.Arguments[0]).Expression).Value

「製品」フィールドを持つオブジェクトタイプのようですが、もっと簡単な解決策が必要だと思います。

助言がありますか。

アップデート

明確にするために、達成したいことに従ってコードを変更しました。私の実際のアプリケーションでは、同じことを行うクラスが既にありますが、式ツリーはありません。

var helper = new Helper(typeof(MyClass), "Delete", 
    new Type[] { typeof(Product) }, new object[] {product}));

my の主な理由は、Helper<T>コンパイル時にメソッド シグネチャが有効かどうかをチェックすることです。

更新 2

これは私の現在の実装です。リフレクションを使用せずに値にアクセスするより良い方法はありますか?

public Helper(Expression<Func<T, TResult>> expression)
{
    var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;

    this.Type = typeof(T);
    this.Method = body.Method.Name;
    this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray();

    var values = new List<object>();
    foreach(var arg in body.Arguments)
    {
        values.Add(
            (((ConstantExpression)exp.Expression).Value).GetType()
                .GetField(exp.Member.Name)
                .GetValue(((ConstantExpression)exp.Expression).Value);
        );
    }
    this.ArgValues = values.ToArray();
}
4

3 に答える 3

23

この方法はかなりうまくいきます。式の引数の型と値を返します>

    private static KeyValuePair<Type, object>[] ResolveArgs<T>(Expression<Func<T, object>> expression)
    {
        var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
        var values = new List<KeyValuePair<Type, object>>();

        foreach (var argument in body.Arguments)
        {
            var exp = ResolveMemberExpression(argument);
            var type = argument.Type;

            var value = GetValue(exp);

            values.Add(new KeyValuePair<Type, object>(type, value));
        }

        return values.ToArray();
    }

    public static MemberExpression ResolveMemberExpression(Expression expression)
    {

        if (expression is MemberExpression)
        {
            return (MemberExpression)expression;
        }
        else if (expression is UnaryExpression)
        {
            // if casting is involved, Expression is not x => x.FieldName but x => Convert(x.Fieldname)
            return (MemberExpression)((UnaryExpression)expression).Operand;
        }
        else
        {
            throw new NotSupportedException(expression.ToString());
        }
    }

    private static object GetValue(MemberExpression exp)
    {
        // expression is ConstantExpression or FieldExpression
        if (exp.Expression is ConstantExpression)
        {
            return (((ConstantExpression)exp.Expression).Value)
                    .GetType()
                    .GetField(exp.Member.Name)
                    .GetValue(((ConstantExpression)exp.Expression).Value);    
        }
        else if (exp.Expression is MemberExpression)
        {
            return GetValue((MemberExpression)exp.Expression);
        }
        else
        {
            throw new NotImplementedException();
        }
    }
于 2014-06-25T08:09:40.583 に答える
16

引数式をコンパイルし、それを呼び出して値を計算できます。

var values = new List<object>();
foreach(var arg in body.Arguments)
{
    var value = Expression.Lambda(argument).Compile().DynamicInvoke();
    values.Add(value);
}
this.ArgValues = values.ToArray();
于 2015-06-23T20:38:34.267 に答える
-1

ラムダを使用してデリゲートを作成する例を次に示します。オブジェクト インスタンスは、クロージャと呼ばれる C# 機能を使用してデリゲートにカプセル化されます。

MyClass instance = new MyClass();
    //This following line cannot be changed to var declaration 
    //since C# can't infer the type.
Func<Product, bool> deleteDelegate = p => instance.Delete(p);
Product product = new Product();
bool deleted = deleteDelegate(product);

あるいは、自動的に Curry するヘルパーを作成しようとしています。

public class Helper<T>
    where T : new()
{
    public TResult Execute<TResult>(Func<T, TResult> methodLambda)
    {
        var instance = new T();
        return methodLamda(instance);
    }
}

public void Main()
{
    var helper = new Helper<MyClass>();
    var product = new Product();
    helper.Execute(x => x.Delete(product));
}

ただし、この問題は、WCF プロキシの有効期間を処理するためのヘルパー クラスの作成に疑わしいように見えると言わざるを得ません... ...単純に、このアプローチは WCF 固有のコードをドメインにリークするためです。

于 2013-09-06T07:11:00.183 に答える