I want to create parameter (instance of ParameterExpression class), which should implement multiple interfaces (let they are IComparable, IFormattable) like that:
//TypeWithIComparableIFormattable composite_type = ...;
ParameterExpression parameter = Parameter(composite_type, "composite_param");
// Usage specific methods of composite param from implemented interfaces
Despite the fact that a variable/parameter can be an instance of different types, you cannot delcare it like this:
IComparable, IFormattable obj;
To do multi-interface implementation check, you may:
Delcare your own interface, say IComparableAndFormattable
, inheriting from IComparable
and IFormattable
. But this approach requires that the type of parameter must implement the interface.
Perform a runtime check. It's wordy, but keeps the code staying flexible:
Use Expression.TypeAs()
method to convert your parameter to the desired type.
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());
where ThrowNotTypeOf
is a helper method generating a throw new ArgumentException
statement:
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));
}