Ошибка в компиляторе C# 8.0 или во время выполнения .NET Core 3?
Я думаю, что, возможно, обнаружил проблему либо с компилятором C# 8.0, либо со средой выполнения.NET Core в отношении реализаций членов интерфейса по умолчанию и ограничений параметров универсального типа.
Общая суть в том, что я реализовал очень простой дизайн, который вы можете использовать для воспроизведения исключения 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>
-методы объявляют ограничение универсального типа для TMessageHandler
который также ссылается на параметр универсального типа интерфейса TMessage
.
В тестовый проект я добавил заглушку-реализацию IMessageHandler<TMessage>
интерфейс (SomeHandler
), чтобы иметь некоторый тип, который удовлетворяет ограничению параметра универсального типа. Затем я реализовал следующий простой тест, который вызывает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
.
Также обратите внимание, что если я сохраню ограничение параметра универсального типа без изменений, но перенесу реализацию метода в SomeClass<TMessage>
- это то, что вы делали бы до C# 8.0 - тогда код также работает нормально, так что именно эта конкретная комбинация ограничений и реализации метода интерфейса по умолчанию заставляет систему ломаться.
Это ошибка компилятора или среды CLR, или я упускаю важный шаг в моем мыслительном процессе?
2 ответа
Я считаю, что это дубликат ошибки, о которой первоначально сообщалось в Roslyn, которая затем была признана ошибкой в CLR.
Похоже, что это еще не исправлено, но в.NET 5.0 есть важная веха.