假设我有以下表达式:
Expression<Action<T, StringBuilder>> expr1 = (t, sb) => sb.Append(t.Name);
Expression<Action<T, StringBuilder>> expr2 = (t, sb) => sb.Append(", ");
Expression<Action<T, StringBuilder>> expr3 = (t, sb) => sb.Append(t.Description);
我希望能够将这些编译成与以下内容等效的方法/委托:
void Method(T t, StringBuilder sb)
{
sb.Append(t.Name);
sb.Append(", ");
sb.Append(t.Description);
}
最好的方法是什么?我希望它表现良好,理想情况下性能与上述方法相当。
更新 那么,虽然看起来没有办法在C#3中直接执行此操作,有没有办法将表达式转换为IL,以便我可以将它与System.Reflection.Emit一起使用?
答案 0 :(得分:4)
不幸的是,在.NET 3.5中,您无法构建执行一系列任意操作的表达式。以下是支持的表达式列表:
.NET 4通过添加以下表达式来扩展此API:
Block表达特别有趣。
答案 1 :(得分:1)
你可以,但这不是一项微不足道的任务。
如果您有Expression类型的变量,则可以检查其Body属性以查找表达式的数据结构。
你不能要求编译器为你编译它,因为它不会得到你想要的结果。你必须解析所有表达式的主体,并以某种方式将它们组合成一个单独的方法,所有这些都是通过同时发出IL(或者通过生成C#并且如果你觉得IL是一个步骤太过而编译的那样)。
正如LINQ-to-SQL将表达式编译为SQL查询一样,您也可以将表达式编译成您需要的任何表达式。你将面临很多工作,但你只需要实现你想要支持的东西。
在这个相当简单的案例中,我认为没有必要创建自己的LINQ提供程序。您可以使用表达式传递并从那里开始。但我怀疑你的应用程序比这复杂一点。
答案 2 :(得分:1)
在4.0中,由于树中对块操作的支持(尽管不在C#表达式编译器中),因此这更容易。
但是,你可以通过利用StringBuilder
暴露“流畅”API的事实来做到这一点;所以代替Action<T,StringBuilder>
你有一个Func<T,StringBuilder,StringBuilder>
- 如下所示(请注意,在这种情况下,表达这些表达式的实际语法相同):
class Program
{
static void Main()
{
Foo(new MyType { Name = "abc", Description = "def" });
}
static void Foo<T>(T val) where T : IMyType
{
var expressions = new Expression<Func<T, StringBuilder, StringBuilder>>[] {
(t, sb) => sb.Append(t.Name),
(t, sb) => sb.Append(", "),
(t, sb) => sb.Append(t.Description)
};
var tparam = Expression.Parameter(typeof(T), "t");
var sbparam = Expression.Parameter(typeof(StringBuilder), "sb");
Expression body = sbparam;
for (int i = 0; i < expressions.Length; i++)
{
body = Expression.Invoke(expressions[i], tparam, body);
}
var func = Expression.Lambda<Func<T, StringBuilder, StringBuilder>>(
body, tparam, sbparam).Compile();
// now test it
StringBuilder sbInst = new StringBuilder();
func(val, sbInst);
Console.WriteLine(sbInst.ToString());
}
}
public class MyType : IMyType
{
public string Name { get; set; }
public string Description { get; set; }
}
interface IMyType
{
string Name { get; }
string Description { get; }
}
肯定可能来检查树并手动发出IL(或许DynamicMethod
),但你必须做出一些限制复杂性的决定。对于所呈现的代码,我可以在合理的时间内完成它(仍然不是一件容易的事),但如果你期望任何更复杂的Expression
更多你的话。
答案 3 :(得分:0)
你只能在.NET 4中这样做。很抱歉不知道细节。
编辑:
如果您对Reflection.Emit感到满意,则可以发出一个按顺序调用这些表达式的方法。
另一种选择:
创建'do'方法,即:
void Do(params Action[] actions)
{
foreach (var a in actions) a();
}
答案 4 :(得分:0)
另一种看待这个问题的方法是记住委托是多方的;你可以多次组合Action
;
class Program
{
static void Main()
{
Foo(new MyType { Name = "abc", Description = "def" });
}
static void Foo<T>(T val) where T : IMyType {
var expressions = new Expression<Action<T, StringBuilder>>[] {
(t, sb) => sb.Append(t.Name),
(t, sb) => sb.Append(", "),
(t, sb) => sb.Append(t.Description)
};
Action<T, StringBuilder> result = null;
foreach (var expr in expressions) result += expr.Compile();
if (result == null) result = delegate { };
// now test it
StringBuilder sbInst = new StringBuilder();
result(val, sbInst);
Console.WriteLine(sbInst.ToString());
}
}
public class MyType : IMyType
{
public string Name { get; set; }
public string Description { get; set; }
}
interface IMyType
{
string Name { get; }
string Description { get; }
}