0

Looking for resolution on how to assign properties w/ backing fields which I have created dynamically at RT in an instance constructor. The signatures match with compiler generated attributes as auto-properties. Essentially they would be equivalent to the code listed below.

Using .NET Core 2.0

Question: How do I assign the backing fields within the constructor using Emit?

For example:

public class MyClass {
  public MyClass(int f1, string f2) {
    _field1 = f1;
    _field2 = f2;
  }

  private readonly int _field1;
  private readonly string _field2;

  public int Field1 { get; }
  public string Field2 { get; }
}

private static void CreateConstructor(TypeBuilder typeBuilder, IReadOnlyList<dynamic> backingFields) {
  var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new[] {typeof(KeyValuePair<string, string>), typeof(Dictionary<string, Type>)});
  var ctorIL = constructorBuilder.GetILGenerator();

  // Load the current instance ref in arg 0, along
  // with the value of parameter "x" stored in arg X, into stfld.

  for (var x = 0; x < backingFields.Count; x++) {
    ctorIL.Emit(OpCodes.Ldarg_0);
    ctorIL.Emit(OpCodes.Ldarg_S, x+1);
    ctorIL.Emit(OpCodes.Stfld, backingFields[x]);
  }

  ctorIL.Emit(OpCodes.Ret); 
}

  public .cctor(KeyValuePair<string, string> kvp, Dictionary<string, Type> collection) {
    _Name = kvp.Key;
    _JSON = kvp.Value;
    _PropertyInfo = collection;
  }

Iterate over methods defined within interface and create a new properties & accessors w/ private setters in the new Type.

  public interface IComplexType {
    string Name { get; set; }
    string JSON { get; set; }
    object PropertyInfo { get; set; }
  }
Latency
  • 426
  • 3
  • 12
  • Code DOM is significantly easier than Refection Emit –  Oct 15 '17 at 01:57
  • 1
    The code you posted seems to assign the constructor arguments to backing fields. Is it not working? – Titian Cernicova-Dragomir Oct 15 '17 at 02:07
  • Something is not working right. It compiles and returns the type. Able to create an instance.. when inspected, I am missing some fields/properties. For Example, I am supposed to have a class with Interface containing 3 fields. The JSON will append additional fields/properties/methods to the class which works, but I am trying to make the members defind in the interface read only. public interface IComplexType { string Name { get; set; } string JSON { get; set; } [Bindable(true)] [TypeConverter(typeof(StringConverter))] object PropertyInfo { get; set; } } – Latency Oct 15 '17 at 21:13
  • I am not sure the Copy Ctor is correct. Not entirely sure the System.Object and Ld_Arg0 are correct. I am not trying to create a static class. It is one that I was expecting to be instanciated and created like this: dynamic tx = DynamicClass.New(new KeyValuePair(arg.Tokens[0], arg.Tokens[1]), myPropertyInfos); // Where the params are the 3x args used to fill the backingfields within the ctor w/ params. The class does not extend anything, so why is the System.Object being extended in this example? – Latency Oct 15 '17 at 21:24
  • return (T) Activator.CreateInstance(TypeCollection[type], kvp, collection); – Latency Oct 15 '17 at 21:26
  • Basically, if i take out the constructor body and just put a return statement in there, everything works just fine and I am able to create an instance. However, the fields are not getting assigned.. because I haven't defined them since I can't get it to work with the code I mentioned in my 1st comment. Am I storing the fields wrong or using the wrong ldarg_X or ? – Latency Oct 15 '17 at 21:33
  • Always best to look at the MSIL that the C# compiler emits for the class. You forgot to call the base class constructor, the one for System.Object. What kind of side-effects that might have are a bit hard to guess at. – Hans Passant Oct 15 '17 at 22:28

1 Answers1

1

Resolved!

Needed to change the constructor arguments to match the number of iterations since it was harder to Ldarg_1 as KeyValuePair and assign its Key & Value respectivly.

By eliminating the KVP and providing an additional parameter the constructor is defined as follows:

  var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new[] {typeof(string), typeof(string), typeof(Dictionary<string, Type>)});
  var ctorIl = constructorBuilder.GetILGenerator();

  for (var x = 0; x < backingFields.Count; x++) {
    ctorIl.Emit(OpCodes.Ldarg_0);
    ctorIl.Emit(OpCodes.Ldarg_S, x + 1);
    ctorIl.Emit(OpCodes.Stfld, backingFields[x]);
  }

  ctorIl.Emit(OpCodes.Ret);

To invoke, I just extracted the contents of the KVP here:

  return (T) Activator.CreateInstance(TypeCollection[type], kvp.Key, kvp.Value, collection);
Latency
  • 426
  • 3
  • 12