【发布时间】:2016-03-31 19:47:10
【问题描述】:
我使用 CSharpCodeProvider 来构建动态 C#,效果很好。比表达式树稍微快一点,当然比表达式树和 IL 更容易维护......
我遇到了一个似乎无法正常工作的案例。
ConsoleApp1.exe 我的班级
然后我使用 CSharpCodeProvider 生成内存临时 DLL。说,像这样基本的东西:
void TestFunc(MyClass 输入) { }
这会产生编译器错误,因为 CSharpCodeProvider 没有对 MyClass 的引用。由于 MyClass 存在于 EXE 中,因此无法添加对编译器对象的引用。我收到一条错误消息,提示它找不到 ConsoleApp1 的元数据。
有没有办法让代码在 EXE 的上下文中存在?或者有什么方法可以使参考工作?我知道如果我将 MyClass 移动到 DLL 中然后将其添加为引用,它会起作用,但这对于我的班级的用户来说是不可接受的要求。
编辑:
所以,使用纯表达式树,我生成了这个方法:
.Lambda #Lambda1<System.Func`2[ConsoleApplication2.Source,ConsoleApplication2.Dest]>(ConsoleApplication2.Source $var1) {
.New ConsoleApplication2.Dest(){
S1 = .Call $var1.get_S1(),
S2 = .Call $var1.get_S2(),
I1 = .Call $var1.get_I1(),
I2 = .Call $var1.get_I2(),
S3 = .Call $var1.get_S3(),
S4 = .Call $var1.get_S4(),
S5 = .Call $var1.get_S5()
}
}
编译为 lambda,调用 1M 次需要 ~285ms。
“编译器”方式,我生成这段代码:
namespace ConsoleApplication2
{
public class TestClass
{
public static ConsoleApplication2.Dest TestFunc(ConsoleApplication2.Source source)
{
ConsoleApplication2.Dest d = new ConsoleApplication2.Dest();
d.S1 = source.S1;
d.S2 = source.S2;
d.I1 = source.I1;
d.I2 = source.I2;
d.S3 = source.S3;
d.S4 = source.S4;
d.S5 = source.S5;
return d;
}
}
}
然后我构建了一个类型化的 lambda:
ParameterExpression p1 = Expression.Parameter(typeof(Source));
var v = Expression.Lambda<Func<Source, Dest>>(Expression.Call(mi, p1), new ParameterExpression[] { p1 }).Compile();
当我调用 v(obj) 1M 次时,只需要 133ms 是纯表达式树时间的 1/2。
我只是犹豫要不要走这条路,因为要求类必须在 EXE 甚至 DLL 中是公共的。
知道为什么表达式树这么慢吗?
【问题讨论】:
-
听起来你有一个先有鸡还是先有蛋的问题——你在可执行文件中构建动态 DLL,但动态 DLL 需要引用可执行文件才能编译。如果是这样,答案是将
MyClass移动到控制台应用程序和动态 DLL 都引用的类库。或者回到使用表达式树:) -
你能给我们看一些代码吗?具体来说 MyClass 定义/编译成什么?将可执行文件作为参考程序集应该没有问题。
-
@DStanley - 嗯......我的错......显然你可以添加一个EXE作为参考,但你必须专门将.EXE附加到程序集名称。此外,类型必须在 EXE 中公开。这样就解决了这部分问题,但并没有增加太多性能。
-
@mikez 是的,我确实让它工作了......虽然很奇怪......我将代表包裹在表达式 lambda 中,我的工作台是 133 毫秒,使用纯表达式树是 288 毫秒.不知道为什么纯表达式树需要更长的时间......
-
您担心 1M 呼叫需要额外的 150 毫秒吗?这对应用程序的整体性能是否重要?
标签: c# .net compiler-construction