Этот фрагмент кода является упрощенным извлечением моего кода генерации кода, который создает два класса, которые ссылаются друг на друга как аргументы в родовом типе:
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
завершается успешно. Поэтому исключение специально вызвано использованием типа, который еще не был создан в качестве аргумента в родовом типе поля; однако, поскольку мне нужно использовать циклическую ссылку, я не могу избежать этой ситуации, создав типы в определенном порядке. - Да, мне нужно использовать круговую ссылку.
- Удаление оболочки
TestGeneric<>
и объявление полей какTypeOne
иTypeTwo
напрямую не вызывает эту ошибку; поэтому я могу использовать динамические типы, которые были определены, но не созданы. - Изменение
TestGeneric<>
отstruct
доclass
не вызывает этой ошибки; поэтому этот шаблон работает с большинством дженериков, а не с типичными типами значений. - Я не могу изменить объявление
TestGeneric<>
в моем случае, поскольку он объявлен в другой сборке, а именноSystem.Data.Linq.EntityRef<>
, объявленный в System.Data.Linq.dll. - Моя круговая ссылка вызвана представлением двух таблиц с ссылками на внешние ключи друг к другу; следовательно, потребность в этом конкретном родовом типе и этой конкретной модели.
- Успешная смена круговой ссылки на самоописание edit. Это произошло неудачно из-за того, что у меня был
TestGeneric<>
как вложенный тип в программе, поэтому он унаследовал видимостьinternal
. Я исправил это сейчас в примере кода выше, и он действительно работает. - Также выполняется компиляция сгенерированного кода вручную (как код С#), поэтому это не проблема с неясным компилятором.
Любые идеи по a) почему это происходит, б) как я могу исправить это и/или c) как я могу обойти это?
Спасибо.