Este fragmento de código es un extracto simplificada de mi código de clase generación, lo que crea dos clases que hacen referencia entre sí como argumentos en un tipo genérico:¿Por qué recibo esta excepción al emitir clases que se referencian entre sí a través de genéricos de tipo valor?
namespace Sandbox
{
using System;
using System.Reflection;
using System.Reflection.Emit;
internal class Program
{
private static void Main(string[] args)
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Test");
var typeOne = module.DefineType("TypeOne", TypeAttributes.Public);
var typeTwo = module.DefineType("TypeTwo", TypeAttributes.Public);
typeOne.DefineField("Two", typeof(TestGeneric<>).MakeGenericType(typeTwo), FieldAttributes.Public);
typeTwo.DefineField("One", typeof(TestGeneric<>).MakeGenericType(typeOne), FieldAttributes.Public);
typeOne.CreateType();
typeTwo.CreateType();
Console.WriteLine("Done");
Console.ReadLine();
}
}
public struct TestGeneric<T>
{
}
}
que debe producir MSIL equivalente a lo siguiente:
public class TypeOne
{
public Program.TestGeneric<TypeTwo> Two;
}
public class TypeTwo
{
public Program.TestGeneric<TypeOne> One;
}
Pero en vez emite esta excepción en la línea typeOne.CreateType()
:
System.TypeLoadException was unhandled
Message=Could not load type 'TypeTwo' from assembly 'Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Source=mscorlib
TypeName=TypeTwo
StackTrace:
at System.Reflection.Emit.TypeBuilder.TermCreateClass(RuntimeModule module, Int32 tk, ObjectHandleOnStack type)
at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
at System.Reflection.Emit.TypeBuilder.CreateType()
at Sandbox.Program.Main(String[] args) in C:\Users\aca1\Code\Sandbox\Program.cs:line 20
interés Cosas a tener en cuenta:
- La referencia circular no es necesaria para causar la excepción; si no defino el campo
One
enTypeTwo
, crearTypeOne
antes de queTypeTwo
aún falle, pero la creación deTypeTwo
antes deTypeOne
se realiza correctamente. Por lo tanto, la excepción se debe específicamente al uso de un tipo que aún no se ha creado como un argumento en un tipo de campo genérico; sin embargo, debido a que necesito usar una referencia circular, no puedo evitar esta situación creando los tipos en un orden específico. - Sí, I do necesito usar una referencia circular.
- Extracción de la envoltura
TestGeneric<>
tipo y declarando los campos comoTypeOne
&TypeTwo
directamente no produce este error; por lo tanto, I puede utilizar tipos dinámicos que se han definido pero no creado. - Cambiando
TestGeneric<>
destruct
aclass
no produce este error; por lo que este patrón funciona con con la mayoría de los genéricos, simplemente no con tipos de valores genéricos. - No puedo cambiar la declaración de
TestGeneric<>
en mi caso como se declara en otro ensamblado, específicamente,System.Data.Linq.EntityRef<>
declarado en System.Data.Linq.dll. - Mi referencia circular se produce al representar dos tablas con referencias de clave externa entre sí; de ahí la necesidad de ese tipo genérico específico y este patrón específico.
- Cambiando la referencia circular a una autorreferencia edit tiene éxito. Esto falló originalmente porque tenía
TestGeneric<>
como un tipo anidado en el Programa, por lo que heredó la visibilidadinternal
. He solucionado esto ahora en el ejemplo de código anterior, y de hecho funciona. - La compilación del código generado manualmente (como código C#) también funciona, por lo que no es un problema oscuro del compilador.
Alguna idea sobre a) por qué ocurre esto, b) cómo puedo solucionar esto y/o c) cómo puedo evitarlo?
Gracias.
interesante; en realidad está creando typeTwo dentro de TypeResolver, lo que hace que se cree a mitad de camino a través de typeOne que se está creando, pero lo suficientemente tarde como para que el tiempo de ejecución maneje a typeOne existe. Un problema para este código es que ambos tipos deben estar completamente definidos antes de que cualquiera pueda ser creado; Estoy haciendo eso en este ejemplo, pero debo asegurarme de hacerlo cumplir en mi código de producción. En cualquier caso, esto resuelve mi problema, ¡así que gracias! – FacticiusVir
Me alegra ayudar. Este fue un problema particularmente divertido/interesante de mirar. De hecho, los tipos que están involucrados en la relación circular deben estar listos para la creación en el mismo momento. Gracias por señalar eso. – Scott