2

I'm using the system. reflection.Emit to implement google protocol message

enter image description here

I want to init a static field

class MessageA
{
   static MessageParser<MessageA> Parser = new MessageParser<MessageA>(()=>new MessageA())
}

now MessageA is not created then i tried to get the constructor of Func it throws System.NotSupportedException with Message “Specified method is not supported.” here my example

using System.Reflection;
using System.Reflection.Emit;

Console.WriteLine("Hello, World!");
var assembleBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Dynamic"), AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = assembleBuilder.DefineDynamicModule("Dynamic.dll");
var typeA = moduleBuilder.DefineType("classA");
typeA.DefineDefaultConstructor(MethodAttributes.Public);

typeof(Func<>).MakeGenericType(typeA).GetConstructors();

How does the compiler do it?

vimuth
  • 5,064
  • 33
  • 79
  • 116
Conner
  • 21
  • 4
  • 1
    `GetConstructors` will get the constructors defined for `Func`, which of course don't exist. However you don't need the constructor of a delegate, but a delegate for your constructor, do you? Does this answer your question: https://stackoverflow.com/questions/10593630/create-delegate-from-constructor? – MakePeaceGreatAgain Jul 14 '22 at 14:52
  • how can i get the constructor of Func i need to create a delegate under the IL code – Conner Jul 14 '22 at 17:27
  • @MakePeaceGreatAgain _"which of course don't exist."_ can you please elaborate on this? – Guru Stron Jul 14 '22 at 21:52
  • `typeof(Func<>).MakeGenericType(typeA).GetConstructors();` implies, the following is valid: `var f = new Func()`. You cannot **instantiate** delegate using a default-constructor. What you want is not the constructor of a delegate, but a delegate for a constructor. – MakePeaceGreatAgain Jul 15 '22 at 07:47
  • _"implies, the following is valid"_ - I would say that no, it does not. It implies that `Func` can have a constructor and as far as I know everything in C# has at least one. Also I'm not sure why you are talking about a paramaterless one. – Guru Stron Jul 15 '22 at 07:51
  • sure, but not neccessarily a **paramaterless** (=default) one – MakePeaceGreatAgain Jul 15 '22 at 07:51
  • @MakePeaceGreatAgain yes, sure. But I'm not sure that OP needs a paramaterless one. – Guru Stron Jul 15 '22 at 08:04
  • @GuruStron The only constructor for a `Func` accepts another `Func`. Even if OP's class has multiple ctor-args, that's not what the constructor of `Func` is for. – MakePeaceGreatAgain Jul 15 '22 at 08:05
  • @MakePeaceGreatAgain maybe OP wants to generate the same as [compiler does](https://sharplab.io/#v2:D4AQTAjAsAUCDMACciDCiDetE+UkALIgLIAUAlJtrjSAKwA8AlgHYAuAfIgB6IC8iCvy4QA3NRwBfWJKA===) . – Guru Stron Jul 15 '22 at 08:08

2 Answers2

1

To mix existing und to be created types, TypeBuilder provides a few static helper methods. In this case you could use TypeBuilder.GetConstructor:

Console.WriteLine("Hello, World!");
var assembleBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Dynamic"), AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = assembleBuilder.DefineDynamicModule("Dynamic.dll");
var typeA = moduleBuilder.DefineType("classA");
typeA.DefineDefaultConstructor(MethodAttributes.Public);

var desiredCtorDefintion = typeof(Func<>).GetConstructors().First();
var constructedType = typeof(Func<>).MakeGenericType(typeA);
var ctor = TypeBuilder.GetConstructor(constructedType, desiredCtorDefintion);

ctor is of type internal sealed class ConstructorOnTypeBuilderInstantiation : ConstructorInfo source.dot.net

thehennyy
  • 4,020
  • 1
  • 22
  • 31
  • sorry i ingored there is a static method which is TypeBuilder.GetConstructor,https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-define-a-generic-type-with-reflection-emit – Conner Jul 18 '22 at 11:35
0

typeA is not a created type so it is not allowed to do that (typeA.GetConstructors() will also fail). Build type first:

var typeA = moduleBuilder.DefineType("classA");
typeA.DefineDefaultConstructor(MethodAttributes.Public);

// typeA.GetConstructors() will fail
var type = typeA.CreateType();

type.GetConstructors(); // typeA.GetConstructors() works too
typeof(Func<>).MakeGenericType(type).GetConstructors();

But to create a Func<T> from a constructor you actually don't need the func's constructor, you should be able to use any of methods described here or here.

P.S.

Useful tool to see how compiler handle thigs is sharplab.io. You can see both decompilation to C# and IL.

This decompilation should represent your scenario.

UPD

I would suggest just to create a helper class (in code) which will do what is needed (i.e. return MessageParser with func returning invocation of default ctor) and then use it to call via emit:

public class Helper
{
    public static MessageParser<T> GetDefaultMessageParser<T>() where T : new() => new MessageParser<T>(() => new T());
}

And generation:

var assembleBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Dynamic"), AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = assembleBuilder.DefineDynamicModule("Dynamic.dll");
var typeA = moduleBuilder.DefineType("classA");
var defaultConstructorBuilder = typeA.DefineTypeInitializer(); // define static ctor
var mpType = typeof(MessageParser<>).MakeGenericType(typeA);

var parserFieldBuilder = typeA.DefineField("Parser", mpType, FieldAttributes.Static | FieldAttributes.Private);
var methodInfo = typeof(Helper)
    .GetMethod(nameof(Helper.GetDefaultMessageParser))
    .MakeGenericMethod(typeA);
var ctorGenerator = defaultConstructorBuilder.GetILGenerator();
ctorGenerator.Emit(OpCodes.Call, methodInfo);
ctorGenerator.Emit(OpCodes.Stsfld, parserFieldBuilder);
ctorGenerator.Emit(OpCodes.Ret);

var type = typeA.CreateType();

var instance = Activator.CreateInstance(type);
var value = type.GetField("Parser",  BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(new object[] { });
Console.WriteLine(value?.ToString()); // prints MessageParser`1[classA]

But in general I would recommend switching to source generators (here my sandbox one, for example) which will allow you to generate your class during compilation with, I would argue, much less effort and less error prone.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132