C# 8.0 编译器或 .NET Core 3 运行-time 中的错误?

Bug in C# 8.0 compiler or .NET Core 3 run-time?

我想我可能发现了 C# 8.0 编译器或 .NET Core 运行-time 关于默认接口成员实现和泛型类型参数约束的问题。

它的一般要点是我实现了一个非常简单的设计,您可以使用它来重现 运行-time VerificiationException 我在 运行ning 一段编译得很好的代码时得到的实际上应该没问题。

那么让我们来看看代码吧。我创建了一个包含两个项目的空白解决方案:一个针对 .NETStandard 2.1 的 C# 库和一个针对 .NET Core 3.1 的 C# 测试项目,其中测试-项目引用库。

然后在库项目中我添加了如下代码:

// 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 在调用 DoSomething<...> 方法时抛出 VerificationException

System.Security.VerificationException:方法 ISomeInterface`1[System.Object].DoSomething:类型参数 'TMessageHandler' 违反了类型参数 'TMessageHandler' 的约束。

就好像 运行 时间看不到 class SomeHandler 实际上 确实 满足这个约束 - 这已经被检查过编译器。

稍作试验后,我注意到如果我将类型参数约束更改为不依赖于 on/use 接口类型参数 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 中的错​​误,还是我在思考过程中遗漏了一个重要步骤?

我认为这是 the bug initially reported against Roslyn which was then resolved to be a bug in the CLR 的副本。

看起来它还没有修复,但有一个里程碑要在 .NET 5.0 中修复。

此问题最近已在 .net 5.0

中得到解决