如何在表达式树中创建需要实现多个接口的参数

本文关键字:实现 接口 参数 表达式 创建 | 更新日期: 2023-09-27 18:06:40

我想创建参数(ParameterExpression类的实例),它应该实现多个接口(让它们是IComparable, IFormattable),像这样:

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

如何在表达式树中创建需要实现多个接口的参数

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

IComparable, IFormattable obj;

要进行多接口实现检查,您可以:

  1. IComparableIFormattable继承你自己的接口,比如IComparableAndFormattable。但是这种方法要求参数的类型必须实现接口。

  2. 执行运行时检查。这是冗长的,但保持了代码的灵活性:

使用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));
}