Почему я получаю общее нарушение ограничения во время выполнения? [closed]

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

new TestServer(8888);

System.TypeLoadException

GenericArguments[0], 'TOutPacket', on     
'Library.Net.Relay`4[TInPacket,TOutPacket,TCryptograph,TEndian]' 
violates the constraint of type parameter 'TInPacket'.

at System.RuntimeTypeHandle.Instantiate(RuntimeTypeHandle handle, IntPtr* pInst, Int32 numGenericArgs, ObjectHandleOnStack type)
at System.RuntimeTypeHandle.Instantiate(Type[] inst)
at System.RuntimeType.MakeGenericType(Type[] instantiation)

Я не понимаю, почему Бывает. Не проверяются ли общие ограничения во время компиляции?

Поиск в Google привел меня к выводу, что это имеет какое-то отношение к одной из этих причин или (иногда?) К обеим:

  • Порядок, в котором определены общие ограничения ( где ) в классах;
  • Использование универсального шаблона со ссылками на себя (интуитивно понятный, но очень законный, см. сообщение в блоге Эрика Липперта )

Я не готов жертвовать собой -соответствующий шаблон. Он мне абсолютно необходим для определенной цели.

Однако мне бы хотелось указать, где и почему возникает эта проблема. Поскольку библиотека огромна и создает огромные общие шаблоны, я думаю, что было бы лучше постепенно выдавать биты кода по запросу.

По запросу, снова объявления. Но я хотел бы подчеркнуть тот факт, что я предпочел бы в целом знать, почему может возникнуть подобное исключение, и затем приступить к его исправлению в моем конкретном коде, а не искать конкретное исправление для потомков. Кроме того, любому, кто анализирует код, потребуется гораздо больше времени, чтобы ответить, чем дать общее объяснение того, почему ограничения универсального типа могут быть нарушены во время выполнения.

Объявления реализации:

class TestServer : Server

class TestClient : AwareClient

class ServerPacket
{
    public abstract class In : AwarePacket.In
    public class Out : OperationPacket.Out
}

public enum TestOperationCode : byte

Объявления библиотеки:

public abstract class Server : IDisposable
    where TServer : Server
    where TClient : Client
    where TInPacket : Packet.In
    where TOutPacket : Packet.Out
    where TCryptograph : Cryptograph, new()
    where TEndian : EndianBitConverter, new()

public abstract class Relay : IDisposable
    where TInPacket : Packet.In
    where TOutPacket : Packet.Out
    where TCryptograph : Cryptograph, new()
    where TEndian : EndianBitConverter, new()

public abstract class Client : Relay, IDisposable
    where TServer : Server
    where TClient : Client
    where TInPacket : Packet.In
    where TOutPacket : Packet.Out
    where TCryptograph : Cryptograph, new()
    where TEndian : EndianBitConverter, new()

public abstract class Packet : ByteBuffer, IDisposable 
    where TEndian : EndianBitConverter, new()
{
    public abstract class In : Packet
    public abstract class Out : Packet
}

public class OperationPacket 
    where TEndian : EndianBitConverter, new()
{
    public class In : Packet.In
    public class Out : Packet.Out
}

public abstract class AwareClient : Client, IDisposable
    where TCryptograph : Cryptograph, new()
    where TInPacket : AwarePacket.In
    where TOutPacket : Packet.Out
    where TServer : Server
    where TClient : AwareClient
    where TEndian : EndianBitConverter, new()

public class AwarePacket
    where TCryptograph : Cryptograph, new()
    where TInPacket : AwarePacket.In
    where TOutPacket : Packet.Out
    where TServer : Server
    where TClient : AwareClient
    where TEndian : EndianBitConverter, new()
{
    public abstract class In : OperationPacket.In
}

Как указано в комментариев, самый простой способ получить помощь по этому вопросу для меня - свести код к небольшому и воспроизводимому примеру, в котором ошибка все еще присутствует. Однако для меня это одновременно и сложно, и долго, и у меня есть высокие шансы превратить ошибку в heisenbug, поскольку это происходит из-за сложности.

Я пытался изолировать ее от следующего, но я не получаю ошибку, когда Я делаю:

// Equivalent of library
class A // Client
    where TA : A
    where TB : B
    where TI : I
    where TO : O
{ }

class B // Server
    where TA : A
    where TB : B
    where TI : I
    where TO : O
{ }

class I { } // Input packet

class O { } // Output packet

// Equivalent of Aware

class Ii : I { } // Aware input packet

class Ai : A // Aware capable client
    where TA : Ai
    where TB : B
    where TI : Ii
    where TO : O
{ }

// Equivalent of implementation

class XI : Ii { }
class XO : O { }

class XA : Ai { }
class XB : B { }

class Program
{
    static void Main(string[] args)
    {
        new XB(); // Works, so bad isolation
    }
}

Gory Details

  1. Анализ исключения показывает, что TOutPacket нарушает TInPacket на Relay .
  2. У нас есть экземпляр Relay TestClient , который реализует AwareClient , который реализует Client , который реализует Relay .
    • AwareClient используется вместе с AwarePacket , поэтому оба конца знают, какой тип клиента получает какой тип пакетов.
  3. Таким образом, мы знаем, что TOutPacket ] в TestClient нарушает TInPacket в TestClient .
  4. Класс, реализующий TOutPacket , - ServerPacket.Out , который является производным от OperationPacket . Этот тип относительно прост с точки зрения универсальных шаблонов, поскольку он предоставляет только перечислимый тип и конечный тип, не делая перекрестных ссылок на другие классы. Заключение: проблема (скорее всего) не в этом объявлении.
  5. Класс, реализующий TInPacket , - это ServerPacket.In , который является производным от AwarePacket . Этот тип намного сложнее, чем TOutPacket , поскольку он перекрестно ссылается на дженерики, чтобы знать ( AwarePacket ) клиента, который его получил. Вероятно, именно в этом беспорядке и возникает проблема.

Тогда многие гипотезы могут сливаться. На данный момент то, что я прочитал, правильно и принято компилятором, но, очевидно, что-то не так.

Можете ли вы помочь мне выяснить, почему я получаю общее нарушение ограничения во время выполнения с моим кодом?

23
задан George Stocker 10 January 2012 в 14:39
поделиться