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
中得到解决
我想我可能发现了 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
中得到解决