0

I think I may have found an issue with either the C# 8.0 compiler or the .NET Core run-time regarding default interface member implementations and generic type parameter constraints.

The general gist of it is that I implemented a very simple design which you can use to reproduce the run-time VerificiationException I get when running a piece of code that compiles just fine and actually should be fine.

So let's get to the code. I created a blank solution with two projects: one C# library targeting .NETStandard 2.1 and one C# test project targeting .NET Core 3.1, where the test-project references the library.

Then in the library project I added the following code:

// 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> { }
    }
}

Note how the DoSomething<TMessageHandler>-methods declare a generic type constraint on TMessageHandler that also references the interface's generic type parameter TMessage.

In the test-project, I added a stub-implementation of the IMessageHandler<TMessage> interface (SomeHandler) to have some type that satisfies the generic type parameter constraint. Then I implemented the following simple test that invokes the ISomeInterface<object>.DoSomething<SomeHandler>'s overload that has the default implementation (note: I use 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> { }
}

As you would expect, this all compiles just fine.

However, when you run this test, the CLR throws a VerificationException at the point of invoking the DoSomething<...>-method:

System.Security.VerificationException: Method ISomeInterface`1[System.Object].DoSomething: type argument 'TMessageHandler' violates the constraint of type parameter 'TMessageHandler'.

VerificationException

It's as if the run-time cannot see that class SomeHandler actually does satisfy this constraint - which is already checked by the compiler.

After experimenting a bit, I noticed the issue goes away if I change the type parameter constraint to something that doesn't depend on/use the interface's type parameter TMessage. For example, if I simply omit the requirement that TMessageHandler implements IMessageHandler<TMessage>, the code runs just fine:

public interface ISomeInterface<TMessage>
{
    void DoSomething<TMessageHandler>() where TMessageHandler : class =>
        DoSomething<TMessageHandler>("Something");

    void DoSomething<TMessageHandler>(string value) where TMessageHandler : class;
}

It's also possible to add other constraints, as long as they don't use TMessage.

Also note that if I keep the generic type parameter constraint intact but move the method's implementation to SomeClass<TMessage> - which is what you would do before C# 8.0 - then the code also runs fine, so it's this particular combination of constraint and default interface method implementation that makes the system crack.

Is this a bug in the compiler or CLR, or am I missing a vital step in my thought-process?

4

2 回答 2

1

I believe this is a duplicate of the bug initially reported against Roslyn which was then resolved to be a bug in the CLR.

It looks like it's not yet fixed, but has a milestone to be fixed in .NET 5.0.

于 2020-04-01T07:20:57.683 回答
1

This issue has been recently resolved in .net 5.0

于 2020-04-15T15:39:46.860 回答