如何发出返回ref的动态方法?

6 c# reflection delegates reflection.emit ref

我正在浏览ref返回的来龙去脉,并且无法发出一个由ref返回的动态方法.

手工制作的lambdas和现有方法按预期工作:

class Widget
{
    public int Length;
}
delegate ref int WidgetMeasurer(Widget w);

WidgetMeasurer GetMeasurerA()
{
    return w => ref w.Length;
}

static ref int MeasureWidget(Widget w) => ref w.Length;
WidgetMeasurer GetMeasurerB()
{
    return MeasureWidget;
}
Run Code Online (Sandbox Code Playgroud)

但是发出动态方法失败了.注意:我在这里使用Sigil.道歉,我不太熟悉System.Reflection.Emit.

WidgetMeasurer GetMeasurerC()
{
    FieldInfo lengthField = typeof(Widget).GetField(nameof(Widget.Length));
    var emitter = Emit<WidgetMeasurer>.NewDynamicMethod()
        .LoadArgument(0)
        .LoadFieldAddress(lengthField)
        .Return();
    return emitter.CreateDelegate();
}
Run Code Online (Sandbox Code Playgroud)

NewDynamicMethod投掷失败了'The return Type contains some invalid type (i.e. null, ByRef)'.这是有道理的,因为我明白在引擎盖下WidgetMeasurer返回一个Int32&.

问题是,是否有一些第一方或第三方技术可以用来发出模仿前两个例子的代码(我凭经验知道它可以正常工作)?如果没有,这个限制是否合乎逻辑?

编辑:我已经尝试了等效的System.Reflection.Emit代码并得到了相同的异常(如预期的那样):

WidgetMeasurer GetMeasurerD()
{
    FieldInfo lengthField = typeof(Widget).GetField(nameof(Widget.Length));

    Type returnType = typeof(int).MakeByRefType();
    Type[] paramTypes = { typeof(Widget) };
    DynamicMethod method = new DynamicMethod("", returnType, paramTypes);

    ILGenerator il = method.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldflda, lengthField);
    il.Emit(OpCodes.Ret);

    return (WidgetMeasurer)method.CreateDelegate(typeof(WidgetMeasurer));
}
Run Code Online (Sandbox Code Playgroud)

Mik*_*ray 4

我不知道为什么 DynamicMethod 存在这种限制,但以下内容对我有用。一个区别是我们手动定义我们自己的动态装配。另一个区别是,由于这是单独的程序集,因此Widget需要公开(或者如果您适当地命名动态程序集,则可以在父程序集上使用InternalsVisibleTo)。

static void Main(string[] args)
{
    var widget = new Widget();
    GetLengthMeasurer()(widget) = 7;
    Console.WriteLine(widget.Length);
}

private static WidgetMeasurer GetLengthMeasurer()
{
    var fieldInfo = typeof(Widget).GetField("Length");
    var asmName = new AssemblyName("WidgetDynamicAssembly." + Guid.NewGuid().ToString());
    var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndCollect);
    var moduleBuilder = asmBuilder.DefineDynamicModule("<Module>");
    var typeBuilder = moduleBuilder.DefineType("WidgetHelper");
    var methodBuilder = typeBuilder.DefineMethod("GetLength", MethodAttributes.Static | MethodAttributes.Public, typeof(int).MakeByRefType(), new[] { typeof(Widget) });
    var ilGen = methodBuilder.GetILGenerator();
    ilGen.Emit(OpCodes.Ldarg_0);
    ilGen.Emit(OpCodes.Ldflda, fieldInfo);
    ilGen.Emit(OpCodes.Ret);
    var type = typeBuilder.CreateType();
    var mi = type.GetMethod(methodBuilder.Name);
    var del = (WidgetMeasurer)mi.CreateDelegate(typeof(WidgetMeasurer));
    return del;
}
Run Code Online (Sandbox Code Playgroud)

输出:

7