0

I try to create a dynamic method in .NET which has an "in" parameter, but it throws an exception in the EmitCall line:

public struct Context
{
    public string MyString { get; set; }
}

public interface IHandler<T>
{
    void MyMethod(in Context context);
}

class MyMessage
{
}

class MyClass : IHandler<MyMessage>
{
    public void MyMethod(in Context context)
    {
    }
}

class ContextMethodInvoker
{
    private readonly InvokeHandler _invoker;

    private delegate void InvokeHandler(object handler, in Context context);

    public ContextMethodInvoker(MethodInfo methodInfo)
    {
        var dynamicMethod = new DynamicMethod(string.Empty,
            typeof(void), new[] { typeof(object), typeof(Context).MakeByRefType() });
        dynamicMethod.DefineParameter(1, ParameterAttributes.None, "handler");
        dynamicMethod.DefineParameter(2, ParameterAttributes.In, "context");

        var il = dynamicMethod.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);

        il.EmitCall(OpCodes.Callvirt, methodInfo, null);
        il.Emit(OpCodes.Ret);

        _invoker = (InvokeHandler)dynamicMethod.CreateDelegate(typeof(InvokeHandler));
    }

    public void Invoke(object handler, in Context context)
    {
        _invoker.Invoke(handler, in context);
    }
}

And:

var messageHandlerType = typeof(IHandler<>);
var messageType = typeof(MyMessage);
var type = typeof(MyClass);

var interfaceType = messageHandlerType.MakeGenericType(messageType);

 
var contextMethod = new ContextMethodInvoker(
    type.GetInterfaceMap(interfaceType).TargetMethods.SingleOrDefault()
);

var tm = new Context
{
    MyString = "test"
};
contextMethod.Invoke(Activator.CreateInstance(type), tm);

What am I doing wrong? When I remove the "in" keywords and the MakeByRefType then it works.

zgabi
  • 404
  • 1
  • 4
  • 11
  • I'm guess that `ParameterAttributes.In` have something to do with COM-interop. To emit same code as C#, you need to change type of parameter from `Context` to `Context&` (managed pointer to `Context`). For runtime there's no difference between `in` and `out` parameters, since they're just references. Compiler is able to differentiate between them by inspecting `System.Runtime.CompilerServices.IsReadOnlyAttribute` attached to parameter – JL0PD Jan 27 '23 at 05:30
  • After giving a closer look at actual code, the problem is that reference to `Context` should be loaded, not reference to where reference is hold. Insert `ldind.ref` after `ldarg.1` – JL0PD Jan 27 '23 at 05:34

0 Answers0