如何将 Expression.Lambda 附加到任何所有者类型?
How to attach Expression.Lambda to ANY owner type?
我希望这个测试通过:
[Test]
public void LambdaTest()
{
var m = Expression.Lambda(typeof(Func<int>), Expression.Constant(0)).Compile();
Assert.That(m.Method.DeclaringType, Is.Not.Null);
}
这是使堆栈遍历滞后代码正常工作所必需的。最简单的方法是什么?
我更喜欢最便携的方式。
lambda 表达式编译为 DynamicMethod,它对于 DeclaringType 属性.
始终为 null
见
如果我能找到解决方法,也会让我的生活更轻松。
您可以在 运行 时构建一个新类型,然后将表达式编译成该类型的方法。
您需要在 运行 时创建一个新程序集和一个新模块。一旦你创建了它们,你就可以使用它们来创建任意数量的类型。这是创建程序集和模块的代码示例:
var assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName {Name = "MyNewAssembly"},
AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MyNewModule");
现在,您可以使用模块生成器来定义一个新类型,如下所示:
var typeBuilder = moduleBuilder.DefineType("MyNewType");
然后是这样的新方法:
var methodBuilder =
typeBuilder.DefineMethod(
"MyNewMethod",
MethodAttributes.Public | MethodAttributes.Static,
typeof(int), //returns an int
new Type[]{}); //takes no parameters
请注意,方法签名应与您的表达式委托类型匹配。
接下来,我们使用CompileToMethod
方法将表达式编译成新方法:
var expression = Expression.Lambda(typeof(Func<int>), Expression.Constant(0));
expression.CompileToMethod(methodBuilder);
我们从类型生成器生成实际类型:
var type = typeBuilder.CreateType();
然后我们使用Delegate.CreateDelegate
方法创建一个委托给新创建的静态方法,像这样:
Func<int> func =
(Func<int>)Delegate.CreateDelegate(
typeof(Func<int>),
type.GetMethod("MyNewMethod"));
int value = func(); //Test
现在 func.Method.DeclaringType
将 return 我们动态创建的类型。
您可以轻松使用此代码生成一些辅助方法,使其易于使用。
好的,我自己找到了它,但我不确定它在 .NET Core 中的工作方式以及哪个框架可能支持也可能不支持。如果您有更好(更优雅或便携)的解决方案,请随时 post 您的回答。
关键是使用CompileToMethod
of Lambda
表达式。
[Test]
public void LambdaTest2()
{
var asm = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("test"), AssemblyBuilderAccess.Run);
var masm = asm.DefineDynamicModule("main");
var type = masm.DefineType("TestType");
var mb = type.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[0]);
// your lambda
ConstantExpression expressionTree = Expression.Constant(0);
Expression.Lambda(typeof(Func<int>), expressionTree).CompileToMethod(mb);
var m = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), type.CreateType().GetMethod("TestMethod"));
Assert.That(m.Method.DeclaringType, Is.Not.Null);
// you can create another in the same module but with another type (because type can't be changed)
var type2 = masm.DefineType("TestType2");
var mb2 = type2.DefineMethod("TestMethod2", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[0]);
// your lambda 2
ConstantExpression expresisonTree2 = Expression.Constant(1);
Expression.Lambda(typeof(Func<int>), expresisonTree2).CompileToMethod(mb2);
var m2 = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), type2.CreateType().GetMethod("TestMethod2"));
Assert.That(m2.Method.DeclaringType, Is.Not.Null);
// check correctness
Assert.That(m(), Is.EqualTo(0));
Assert.That(m2(), Is.EqualTo(1));
}
我希望这个测试通过:
[Test]
public void LambdaTest()
{
var m = Expression.Lambda(typeof(Func<int>), Expression.Constant(0)).Compile();
Assert.That(m.Method.DeclaringType, Is.Not.Null);
}
这是使堆栈遍历滞后代码正常工作所必需的。最简单的方法是什么?
我更喜欢最便携的方式。
lambda 表达式编译为 DynamicMethod,它对于 DeclaringType 属性.
始终为 null见
如果我能找到解决方法,也会让我的生活更轻松。
您可以在 运行 时构建一个新类型,然后将表达式编译成该类型的方法。
您需要在 运行 时创建一个新程序集和一个新模块。一旦你创建了它们,你就可以使用它们来创建任意数量的类型。这是创建程序集和模块的代码示例:
var assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName {Name = "MyNewAssembly"},
AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MyNewModule");
现在,您可以使用模块生成器来定义一个新类型,如下所示:
var typeBuilder = moduleBuilder.DefineType("MyNewType");
然后是这样的新方法:
var methodBuilder =
typeBuilder.DefineMethod(
"MyNewMethod",
MethodAttributes.Public | MethodAttributes.Static,
typeof(int), //returns an int
new Type[]{}); //takes no parameters
请注意,方法签名应与您的表达式委托类型匹配。
接下来,我们使用CompileToMethod
方法将表达式编译成新方法:
var expression = Expression.Lambda(typeof(Func<int>), Expression.Constant(0));
expression.CompileToMethod(methodBuilder);
我们从类型生成器生成实际类型:
var type = typeBuilder.CreateType();
然后我们使用Delegate.CreateDelegate
方法创建一个委托给新创建的静态方法,像这样:
Func<int> func =
(Func<int>)Delegate.CreateDelegate(
typeof(Func<int>),
type.GetMethod("MyNewMethod"));
int value = func(); //Test
现在 func.Method.DeclaringType
将 return 我们动态创建的类型。
您可以轻松使用此代码生成一些辅助方法,使其易于使用。
好的,我自己找到了它,但我不确定它在 .NET Core 中的工作方式以及哪个框架可能支持也可能不支持。如果您有更好(更优雅或便携)的解决方案,请随时 post 您的回答。
关键是使用CompileToMethod
of Lambda
表达式。
[Test]
public void LambdaTest2()
{
var asm = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("test"), AssemblyBuilderAccess.Run);
var masm = asm.DefineDynamicModule("main");
var type = masm.DefineType("TestType");
var mb = type.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[0]);
// your lambda
ConstantExpression expressionTree = Expression.Constant(0);
Expression.Lambda(typeof(Func<int>), expressionTree).CompileToMethod(mb);
var m = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), type.CreateType().GetMethod("TestMethod"));
Assert.That(m.Method.DeclaringType, Is.Not.Null);
// you can create another in the same module but with another type (because type can't be changed)
var type2 = masm.DefineType("TestType2");
var mb2 = type2.DefineMethod("TestMethod2", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[0]);
// your lambda 2
ConstantExpression expresisonTree2 = Expression.Constant(1);
Expression.Lambda(typeof(Func<int>), expresisonTree2).CompileToMethod(mb2);
var m2 = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), type2.CreateType().GetMethod("TestMethod2"));
Assert.That(m2.Method.DeclaringType, Is.Not.Null);
// check correctness
Assert.That(m(), Is.EqualTo(0));
Assert.That(m2(), Is.EqualTo(1));
}