Рассмотрим пример проекта ниже. Учитывая выражение Expression<Func<T, U>>
, я хочу создать выражение, которое можно вызывать для любого T
, который реализует корневой интерфейс (IBase
).
Проблема
Преобразователи выражений ниже кажутся работающими, но при вызове в более сложных выражениях преобразованный Func
генерирует InvalidOperationException
. До сих пор я не смог воспроизвести это. Хотя, глядя на полученное выражение, я вижу, что есть два параметра - t
и Param_0
- которые, как я полагаю, как-то вызывают проблемы.
System.InvalidOperationException: переменная 't' типа 'IBase'
ссылка из области «Имя», но она не определена
Приведенные ниже методы конвертации должны быть улучшены. Пожалуйста, сообщите!
namespace ExpressionTest
{
interface IBase
{
string Name { get; set; }
}
interface IFoo : IBase
{
string Foo { get; set; }
}
interface IBar : IBase
{
string Bar { get; set; }
}
class FooImpl : IFoo
{
public FooImpl()
{
Name = "Name";
Foo = "Foo";
var e1 = Test<IBase, string>(t => t.Name);
var e2 = Test<IFoo, string>(t => t.Foo);
var e3 = Test<IBar, string>(t => t.Bar);
var e4 = Test2<IBase, string>((b, v) => Stuff(b, v));
e4.Compile().Invoke(this, "new");
var name = e1.Compile().Invoke(this);
var foo = e2.Compile().Invoke(this);
// TODO: Use "as" operator instead of cast...
// var bar = e3.Compile().Invoke(this);
}
public void Stuff(IBase b, string v)
{
b.Name = v;
}
public string Name { get; set; }
public string Foo { get; set; }
private Expression<Func<IBase, TProperty>>
Test<T, TProperty>(Expression<Func<T, TProperty>> expr)
where T : IBase
{
var p = Expression.Parameter(typeof(IBase));
var convert = Expression.Convert(p, typeof(T));
var invoke = Expression.Invoke(expr, convert);
var lambda = Expression.Lambda<Func<IBase, TProperty>>(invoke, p);
return lambda;
}
private Expression<Action<IBase, TProperty>>
Test2<T, TProperty>(Expression<Action<T, TProperty>> expr)
where T : IBase
{
var p1 = expr.Parameters.Last();
var p2 = Expression.Parameter(typeof(IBase));
var convert = Expression.Convert(p2, typeof(T));
var invoke = Expression.Invoke(expr, convert, p1);
var lambda = Expression.Lambda<Action<IBase, TProperty>>(invoke, p2, p1);
return lambda;
}
}
class Program
{
static void Main(string[] args)
{
var foo = new FooImpl();
}
}
}
Если промежуточный интерфейс (например, IBar
) не реализован, он должен просто вернуть значение по умолчанию - это не реализовано в моем примере кода.
Обновление
По сути, я хочу подражать этому:
string MyFooExpressionCompiledMethod(IBase b)
{
if (b is IFoo foo)
{
return foo.Foo;
}
return null;
}