如何创建应该在表达式树中实现多个接口的参数?

时间:2016-09-12 07:49:57

标签: c# .net expression-trees

我想创建一个参数(ParameterExpression类的实例),它应该实现多个接口(让它们是IComparable,IFormattable),如下所示:

//TypeWithIComparableIFormattable composite_type = ...;
ParameterExpression parameter = Parameter(composite_type, "composite_param");
// Usage specific methods of composite param from implemented interfaces

1 个答案:

答案 0 :(得分:1)

尽管变量/参数可以是不同类型的实例,但您不能像这样对它进行delcare:

IComparable, IFormattable obj;

要进行多界面实施检查,您可以:

  1. Delcare您自己的界面,说IComparableAndFormattable,继承自IComparableIFormattable。但是这种方法要求参数类型必须实现接口。

  2. 执行运行时检查。它很罗嗦,但保持代码保持灵活性:

  3. 使用Expression.TypeAs()方法将参数转换为所需类型。

    var param = Expression.Parameter(typeof(object), "o");
    // IComparable comparable;
    var comparableDeclare = Expression.Parameter(typeof(IComparable), "comparable");
    // comparable = o as IComparable;
    var comparableAssign = Expression.Assign(comparableDeclare, Expression.TypeAs(param, typeof(IComparable)));
    // if (comparable == (IComparable)null) 
    // {
    //    throw new ArgumentException("The parameter must be a instance of IComparable.", nameof(o));
    // }
    var comparableCheck = Expression.IfThen(Expression.Equal(comparableDeclare, Expression.Constant(null, typeof(IComparable))),
         ThrowNotTypeOf(typeof(IComparable), param.Name));
    
    var formattableDeclare = Expression.Parameter(typeof(IFormattable), "formattable");
    // formattable = o as IFormattable;
    var formattableAssign = Expression.Assign(formattableDeclare, Expression.TypeAs(param, typeof(IFormattable)));
    // if (formattable == (IFormattable)null) 
    // {
    //    throw new ArgumentException("The parameter must be a instance of IFormattable.", nameof(o));
    // }
    var formattableCheck = Expression.IfThen(
        Expression.Equal(formattableDeclare, Expression.Constant(null, typeof(IFormattable))),
        ThrowNotTypeOf(typeof(IFormattable), param.Name));
    
    var block = Expression.Block(
        new [] {
            comparableDeclare, formattableDeclare
        }, // local variables
        comparableAssign, comparableCheck, formattableAssign, formattableCheck);
    
    foreach (var exp in block.Expressions)
    {
        Console.WriteLine(exp);
    }
    
    
    // Compile the expression tree
    var method = Expression.Lambda<Action<object>>(block, param).Compile();
    
    method.Invoke(new ComparableFormattable());
    

    其中ThrowNotTypeOf是生成throw new ArgumentException语句的辅助方法:

    private static Expression ThrowNotTypeOf(Type type, string paramName)
    {
        var ctor = typeof(ArgumentException).GetConstructor(new[] { typeof(string), typeof(string) });
        Debug.Assert(ctor != null);
    
        var messageArg = Expression.Constant($"The parameter must be an instance of '{type.Name}'.");
        var paramArg = Expression.Constant(paramName);
    
        return Expression.Throw(Expression.New(ctor, messageArg, paramArg));
    }