The code below sets the ClasseB
to runtime that inherits from its ClasseA
, in which I added private fields to use in the properties instead of leaving it to the compiler.
The approach I used to have ClasseB
seven the Atributo
property of ClasseA
was adding a new constructor, with one parameter to it, in ClassB.
Tested and working: link
using System;
using System.Reflection;
using System.Reflection.Emit;
public class Program
{
public static void Main()
{
CriaClasseHerdandoOutra();
}
public static void CriaClasseHerdandoOutra() {
AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
// define tipo "ClasseB" herdando de "ClasseA"
Type typeClasseA = Type.GetType("ClasseA");
TypeBuilder tb = mb.DefineType("ClasseB", TypeAttributes.Public, typeClasseA);
// obtém informação de atributos da "ClasseA" que serão setados
// no novo construtor da classe filha "ClasseB"
FieldInfo codigo = typeClasseA.GetField("_codigo", BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo atributo = typeClasseA.GetField("_atributo", BindingFlags.NonPublic | BindingFlags.Instance);
// cria no tipo "ClasseB" o construtor exigido por sua classe base
Type[] parameterTypes = {typeof(int)};
ConstructorBuilder ctor1 = tb.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
parameterTypes);
// adiciona no construtor instruções para setar
// o atributo "Codigo" da classe base "ClasseA"
ILGenerator ctor1IL = ctor1.GetILGenerator();
ctor1IL.Emit(OpCodes.Ldarg_0);
ctor1IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
ctor1IL.Emit(OpCodes.Ldarg_0);
ctor1IL.Emit(OpCodes.Ldarg_1);
ctor1IL.Emit(OpCodes.Stfld, codigo);
ctor1IL.Emit(OpCodes.Ret);
// cria no tipo "ClasseB" um novo construtor,
// que recebe dois parâmetros
Type[] parameterTypes2 = { typeof(int), typeof(string)};
ConstructorBuilder ctor2 = tb.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
parameterTypes2);
// adiciona no novo construtor instruções para setar
// os atributos "Codigo" e "Atributo" da classe base "ClasseA"
ILGenerator ctor2IL = ctor2.GetILGenerator();
ctor2IL.Emit(OpCodes.Ldarg_0);
ctor2IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
ctor2IL.Emit(OpCodes.Ldarg_0);
ctor2IL.Emit(OpCodes.Ldarg_1);
ctor2IL.Emit(OpCodes.Stfld, codigo);
ctor2IL.Emit(OpCodes.Ldarg_0);
ctor2IL.Emit(OpCodes.Ldarg_2);
ctor2IL.Emit(OpCodes.Stfld, atributo);
ctor2IL.Emit(OpCodes.Ret);
// cria o tipo que foi definido para "ClasseB"
Type ClasseBType = tb.CreateType();
// cria uma instância da "ClasseB", que acaba de ser definida
// em runtime (herdando de "ClasseA"), invocando seu novo construtor
// de dois parâmetros.
ClasseA objClasseB = (ClasseA)System.Activator.CreateInstance(ClasseBType, 1, "Rá!");
// invoca o método "Concatena" da classe base "ClasseA",
// o qual teve seu resultado afetado pela classe filha "ClasseB"
Console.WriteLine(objClasseB.Concatena());
}
}
public class ClasseA
{
protected string _atributo;
protected int _codigo;
protected string Atributo
{
get {return _atributo;}
set {_atributo = value; }
}
public int Codigo
{
get {return _codigo;}
set {_codigo = value; }
}
public ClasseA (int codigo)
{
Codigo = codigo;
}
public string Concatena()
{
return this.Atributo + " - " + this.Codigo;
}
}
Reference: AssemblyBuilder documentation .
The key in this example is the TypeBuilder
classes, for definition of a type in runtime, and ILGenerator
, to add instructions to the body of the defined methods.
There are other ways to achieve similar results. Namely - CodeDOM and Roslyn , released in April this year ( an example ).
Considerations about my code
Of course, this code is for teaching purposes only and needs to be much more readable if used. You can also generalize this code and abstract it into a cooler API that meets a specific need.
Also, I suspect you'd use Attributes . Net to identify which properties to change instead of doing so based on the name.
A typical example of using this type of engineering (creating class inheritance at runtime or project compilation) is in frameworks that do not want to force the user to inherit their classes but need to add resources to the user objects (< in> ORM s, for example).