Этот фрагмент кода является упрощенным фрагментом моего кода генерации классов, в котором создаются два класса, которые ссылаются друг на друга как аргументы в универсальном типе:
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>
{
}
}
, который должен выдавать MSIL, эквивалентный следующему:
public class TypeOne
{
public Program.TestGeneric<TypeTwo> Two;
}
public class TypeTwo
{
public Program.TestGeneric<TypeOne> One;
}
Но вместо этого выбрасывает это исключение в строке 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
Интересно отметить:
- Циклическая ссылка нетребуется вызвать исключение;если я не определяю поле
One
для TypeTwo
, создание TypeOne
до TypeTwo
все еще не выполняется, но создание TypeTwo
до TypeOne
завершается успешно.Следовательно, исключение специально вызывается использованием типа, который еще не был создан в качестве аргумента в универсальном типе поля;однако, поскольку мне нужно использовать циклическую ссылку, я не могу избежать этой ситуации, создавая типы в определенном порядке. - Да, мне do необходимо использовать циклическую ссылку.
- Удаление обертки типа
TestGeneric<>
и объявление полей как TypeOne
& TypeTwo
напрямую не приводит к этой ошибке;таким образом, я могу использовать динамические типы, которые были определены, но не созданы. - Изменение
TestGeneric<>
с struct
на class
не приводит к этой ошибке;так что этот шаблон работает с большинством обобщенных типов, но не с универсальными типами значений. - Я не могу изменить объявление
TestGeneric<>
в моем случае, так как оно объявлено в другой сборке, в частности, System.Data.Linq.EntityRef<>
объявлено в System.Data.Linq.dll. - Моя круговая ссылка вызвана представлением двух таблиц с ссылками на внешние ключи друг на друга;отсюда необходимость в этом конкретном родовом типе и этом конкретном шаблоне.
- Изменение циклической ссылки на собственную ссылку edit завершается успешно.Первоначально это не удалось, потому что у меня был
TestGeneric<>
как вложенный тип в Программе, поэтому он унаследовал видимость internal
.Я исправил это сейчас в приведенном выше примере кода, и он действительно работает. - Компиляция сгенерированного кода вручную (как код C #) также работает, поэтому это не проблема неясного компилятора.
Есть идеи: а) почему это происходит, б) как я могу это исправить и / или в) как я могу обойти это?
спасибо.