Почему я получение этого исключения при создании классов, которые ссылаются друг на друга через универсальные типы значений?

Этот фрагмент кода представляет собой упрощенный фрагмент моего кода создания классов, который создает два класса, которые ссылаются друг на друга как аргументы в универсальном типе:

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 на класс ] не вызывает этой ошибки; так что этот шаблон действительно работает с большинством универсальных типов, но не с универсальными типами значений.
  • Я не могу изменить объявление TestGeneric <> в моем случае, поскольку оно объявлено в другом сборка - в частности, System.Data.Linq.EntityRef <> , объявленная в System.Data.Linq.dll.
  • Моя циклическая ссылка вызвана представлением двух таблиц со ссылками внешнего ключа друг на друга; отсюда необходимость в этом конкретном универсальном типе и этом конкретном шаблоне.
  • Изменение циклической ссылки на ссылку на себя редактирование выполняется успешно. Изначально это не удалось, потому что у меня был TestGeneric <> в качестве вложенного типа в Program, поэтому он унаследовал внутреннюю видимость. Я исправил это сейчас в примере кода выше, и он действительно работает.
  • Компиляция сгенерированного кода вручную (как код C #) также работает, так что это не скрытая проблема компилятора.

Любые идеи по поводу ) почему это происходит, б) как я могу это исправить и / или в) как я могу это решить?

Спасибо.

16
задан FacticiusVir 18 July 2011 в 19:40
поделиться