This code snippet is a simplified extract of my class-generation code, which creates two classes that reference each other as arguments in a generic type:
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>
{
}
}
Which should produce MSIL equivalent to the following:
public class TypeOne
{
public Program.TestGeneric<TypeTwo> Two;
}
public class TypeTwo
{
public Program.TestGeneric<TypeOne> One;
}
But instead throws this exception on the line 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
Interesting things to note:
- The circular reference isn't required to cause the exception; if I don't define field
One
onTypeTwo
, creatingTypeOne
beforeTypeTwo
still fails, but creatingTypeTwo
beforeTypeOne
succeeds. Therefore, the exception is specifically caused by using a type that has not yet been created as an argument in a generic field type; however, because I need to use a circular reference, I cannot avoid this situation by creating the types in a specific order. - Yes, I do need to use a circular reference.
- Removing the wrapper
TestGeneric<>
type and declaring the fields asTypeOne
&TypeTwo
directly does not produce this error; thus I can use dynamic types that have been defined but not created. - Changing
TestGeneric<>
from astruct
to aclass
does not produce this error; so this pattern does work with most generics, just not generic value types. - I can't change the declaration of
TestGeneric<>
in my case as it is declared in another assembly - specifically,System.Data.Linq.EntityRef<>
declared in System.Data.Linq.dll. - My circular reference is caused by representing two tables with foreign key references to each other; hence the need for that specific generic type and this specific pattern.
- Changing the circular reference to a self-reference edit succeeds. This failed originally because I had
TestGeneric<>
as a nested type in Program, so it inherited theinternal
visibility. I've fixed this now in the code sample above, and it does in fact work. - Compiling the generated code manually (as C# code) also works, so it's not an obscure compiler issue.
Any ideas on a) why this occuring, b) how I can fix this and/or c) how I can work around it?
Thanks.