Я думаю, что, возможно, обнаружил проблему либо с компилятором C# 8.0, либо с NET средой выполнения ядра относительно реализаций элементов интерфейса по умолчанию и общих параметров типа c.
Общая суть в том, что я реализовал очень простой дизайн, который можно использовать для воспроизведения исключения VerificiationException во время выполнения, которое я получаю при запуске фрагмента кода, который прекрасно компилируется и на самом деле должно быть отлично.
Итак, давайте перейдем к коду. Я создал пустое решение с двумя проектами: один C# библиотека с таргетингом .NETStandard 2.1 и один C# тестовый проект с таргетингом . NET Core 3.1 , где тест-проект ссылается на библиотеку.
Затем в проект библиотеки я добавил следующий код:
// In library project
namespace SomeLibrary
{
public interface IMessageHandler<TMessage> { }
public interface ISomeInterface<TMessage>
{
void DoSomething<TMessageHandler>() where TMessageHandler : class, IMessageHandler<TMessage> =>
DoSomething<TMessageHandler>("Something");
void DoSomething<TMessageHandler>(string value) where TMessageHandler : class, IMessageHandler<TMessage>;
}
public sealed class SomeClass<TMessage> : ISomeInterface<TMessage>
{
public void DoSomething<TMessageHandler>(string value) where TMessageHandler : class, IMessageHandler<TMessage> { }
}
}
Обратите внимание, как DoSomething<TMessageHandler>
-методы объявляют ограничение типа c на TMessageHandler
он также ссылается на параметр типа generi c интерфейса TMessage
.
. В тестовом проекте я добавил реализацию-заглушку интерфейса IMessageHandler<TMessage>
(SomeHandler
), чтобы иметь некоторый тип, удовлетворяющий ограничение параметра типа c. Затем я реализовал следующий простой тест, который вызывает перегрузку ISomeInterface<object>.DoSomething<SomeHandler>
с реализацией по умолчанию (примечание: я использую MS Test ):
// In test-project.
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace SomeLibrary.Tests
{
[TestClass]
public sealed class SomeClassTest
{
[TestMethod]
public void DoSomething_DoesSomething()
{
CreateSomeClass<object>().DoSomething<SomeHandler>();
}
private static ISomeInterface<TMessage> CreateSomeClass<TMessage>() =>
new SomeClass<TMessage>();
}
public sealed class SomeHandler : IMessageHandler<object> { }
}
Как и следовало ожидать, все это прекрасно компилируется.
Однако, когда вы запускаете этот тест, CLR выбрасывает VerificationException
в момент вызова DoSomething<...>
-метода:
System. Security.VerificationException: метод ISomeInterface`1 [System.Object] .DoSomething: тип аргумента 'TMessageHandler' нарушает ограничение параметра типа 'TMessageHandler'.
Как будто среда выполнения не может видеть, что класс SomeHandler
на самом деле удовлетворяет этому ограничению - что уже проверено компилятором.
После экспериментов, Я заметил, что проблема исчезнет, если я изменю ограничение параметра типа на то, что не зависит от / использует параметр типа интерфейса TMessage
. Например, если я просто опущу требование, что TMessageHandler
реализует IMessageHandler<TMessage>
, код работает просто отлично:
public interface ISomeInterface<TMessage>
{
void DoSomething<TMessageHandler>() where TMessageHandler : class =>
DoSomething<TMessageHandler>("Something");
void DoSomething<TMessageHandler>(string value) where TMessageHandler : class;
}
Также возможно добавить другие ограничения, если они не используются TMessage
.
Также обратите внимание, что если я сохраню ограничение параметра типа generi c в неизменном виде, но перенесу реализацию метода в SomeClass<TMessage>
- это то, что вы сделали бы до C# 8.0 - тогда код также работает нормально, поэтому именно эта комбинация ограничений и реализации метода интерфейса по умолчанию делает систему взломанной.
Это ошибка в компиляторе или CLR, или я пропускаю важный шаг в моем мыслительном процессе?