-
-
Save TheOtherBlack/33cb7d57d94a5feeaa956e01c07dfbc3 to your computer and use it in GitHub Desktop.
Second Part for Lowlevel IL - Defining our type with properties and methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
public interface IMagicType | |
{ | |
string MyAutoProperty { get; set; } | |
string DoStuffWithMyProperty(); | |
} | |
public class MagicTypeA : IMagicType | |
{ | |
public string MyAutoProperty { get; set; } | |
public string DoStuffWithMyProperty() | |
{ | |
MyAutoProperty = string.Format("Hello world! 123"); | |
return MyAutoProperty; | |
} | |
} | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Console.WriteLine("We have MagicTypeA with MyAutoProperty, it's a compiled version from C#:"); | |
var typeA = new MagicTypeA(); | |
Console.WriteLine("Called DoStuffWithMyProperty(): {0}", typeA.DoStuffWithMyProperty()); | |
Console.WriteLine("Now attempting to implement the same as above in Runtime IL!"); | |
// Define a new Dynamic Assembly to store our emitted types and code in | |
var dynaAssemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("MyDynamicAssembly"), AssemblyBuilderAccess.Run); | |
// Every assembly need a module! | |
var moduleBuilder = dynaAssemblyBuilder.DefineDynamicModule("MyDynamicModule"); | |
// And let's define our new type that implements the interface matching that of IMagicType | |
var magicTypeBuilder = moduleBuilder.DefineType("MagicTypeB", TypeAttributes.Public, null, new[] { typeof(IMagicType) }); | |
// Let's define private field so it could be used by our property! | |
var fieldForPropertyBuilder = magicTypeBuilder.DefineField("magicField", typeof(string), FieldAttributes.Private); | |
// Let's define our Property! | |
var propertyBuilder = magicTypeBuilder.DefineProperty("MyAutoProperty", PropertyAttributes.HasDefault, typeof(string), null); | |
// Let's define the Getter Method | |
var getterMethodBuilder = magicTypeBuilder.DefineMethod( | |
"GetProperty", | |
// This is publicly accessible method through Property | |
// This is set to virtual to allow this function to override Interface method | |
MethodAttributes.Public | MethodAttributes.Virtual, | |
typeof(string), | |
Type.EmptyTypes | |
); | |
var il = getterMethodBuilder.GetILGenerator(); | |
il.Emit(OpCodes.Ldarg_0); // Load 'This' onto stack | |
il.Emit(OpCodes.Ldfld, fieldForPropertyBuilder); // Load 'magicField' value onto stack | |
il.Emit(OpCodes.Ret); // Return 'magicField' value | |
propertyBuilder.SetGetMethod(getterMethodBuilder); // Make that our getter method! | |
// Let's define the Setter Method | |
var setterMethodBuilder = magicTypeBuilder.DefineMethod( | |
"SetProperty", | |
// This is publicly accessible method through Property | |
// This is set to virtual to allow this function to override Interface method | |
MethodAttributes.Public | MethodAttributes.Virtual, | |
typeof(void), | |
new[] { typeof(string) } | |
); | |
il = setterMethodBuilder.GetILGenerator(); | |
il.Emit(OpCodes.Ldarg_0); // Load 'This' onto stack | |
il.Emit(OpCodes.Ldarg_1); // Load new value paramter onto stack | |
il.Emit(OpCodes.Stfld, fieldForPropertyBuilder); // Store new value parameter in 'magicField' | |
il.Emit(OpCodes.Ret); // Return nothing | |
propertyBuilder.SetSetMethod(setterMethodBuilder); // Make that our setter method! | |
// Override Interface Methods for Property Getter and Setter | |
var interfaceProperty = typeof(IMagicType).GetProperty("MyAutoProperty"); | |
magicTypeBuilder.DefineMethodOverride(getterMethodBuilder, interfaceProperty.GetMethod); | |
magicTypeBuilder.DefineMethodOverride(setterMethodBuilder, interfaceProperty.SetMethod); | |
// Let's define our DoStuffWithMyProperty! | |
var doMagicMethod= magicTypeBuilder.DefineMethod("DoStuffWithMyProperty", MethodAttributes.Public | MethodAttributes.Virtual, typeof(string), null); | |
il = doMagicMethod.GetILGenerator(); | |
il.Emit(OpCodes.Ldarg_0); // Load This onto stack | |
il.Emit(OpCodes.Dup); // Duplicate the last added item in stack | |
il.Emit(OpCodes.Ldstr, "Hello world! 123"); // Load a constant string reference to "Hello world! 123" onto stack | |
il.Emit(OpCodes.Stfld, fieldForPropertyBuilder); // Pop both This and String Reference above Store string reference into field | |
il.Emit(OpCodes.Ldfld, fieldForPropertyBuilder); // Pop the very first This item and add value of field onto stack | |
il.Emit(OpCodes.Ret); // Pop field value off from stack and return it! | |
// Finally override Interface method for DoStuffWithMyProperty | |
magicTypeBuilder.DefineMethodOverride(doMagicMethod, typeof(IMagicType).GetMethod("DoStuffWithMyProperty")); | |
// Create our type by constructing all of the pieces above | |
var ourNewType = magicTypeBuilder.CreateType(); | |
// Create a new instance and you can access it through the use of Interface with ease. | |
var MagicTypeB = (IMagicType)Activator.CreateInstance(ourNewType); | |
Console.WriteLine("We have MagicTypeB with MyAutoProperty, it's emitted during runtime:"); | |
Console.WriteLine("Called DoStuffWithMyProperty(): {0}", MagicTypeB.DoStuffWithMyProperty()); | |
Console.ReadLine(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment