Ошибка в компиляторе 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 есть важная веха.

Эта проблема была недавно решена в .net 5.0.

Другие вопросы по тегам