dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.56k stars 4.54k forks source link

Inconsistent behavior using interface with generic out and derived classes with explicit implementations #103365

Open chschrae opened 3 weeks ago

chschrae commented 3 weeks ago

Description

When using an interface with a generic out type, an explicit implementation, and a derived class, the base classes implementation is called instead of the derived class when running on Android. Running on Windows yields the expected behavior.

Reproduction Steps

Given an interface:

public interface IBaseInterface<out T>
{
    public void explicitDeclaration();
}

and a couple of classes:

public class BasicBaseClass : IBaseInterface<BasicBaseClass>
{
    string className = "BasicBaseClass";
    void IBaseInterface<BasicBaseClass>.explicitDeclaration()
    {
        // This is a test
        System.Diagnostics.Debug.WriteLine($"explicitDeclaration from {className}");
    }
}

public class BasicDerivedClass : BasicBaseClass, IBaseInterface<BasicDerivedClass>
{
    string className = "BasicDerivedClass";
    void IBaseInterface<BasicDerivedClass>.explicitDeclaration()
    {
        // This is a test
        System.Diagnostics.Debug.WriteLine($"explicitDeclaration from {className}");
    }
}

You can then create some objects in a list and iterate over them to call their function:

var list = new List<IBaseInterface<BasicBaseClass>>();
list.Add(new BasicBaseClass());
list.Add(new BasicDerivedClass());

foreach (var item in list)
{
    item.explicitDeclaration();
}

Expected behavior

I would expect the behavior to be the same on Android and Windows.

Actual behavior

The printouts you will see when running on Windows is:

explicitDeclaration from BasicBaseClass
explicitDeclaration from BasicDerivedClass

and when running on Android:

[0:] explicitDeclaration from BasicBaseClass
[0:] explicitDeclaration from BasicBaseClass

Regression?

No response

Known Workarounds

No response

Configuration

.NET 8 Android 33 and Windows x64

Other information

This only occurs with explicit interface declarations. If you simply hide the base implementation, the issue does not occur. This is true for implicit hiding, 'new' keyword, and overriding virtual functions.

kg commented 3 weeks ago

Based on local testing, it looks like both mono x86 jit and mono interpreter also do this incorrectly.

kg commented 3 weeks ago

As an interim workaround, a construction like this appears to work:

public class BasicDerivedClass : BasicBaseClass, IBaseInterface<BasicBaseClass>, IBaseInterface<BasicDerivedClass>
{
    string className = "BasicDerivedClass";
    string IBaseInterface<BasicBaseClass>.explicitDeclaration()
    {
        return className;
    }

    string IBaseInterface<BasicDerivedClass>.explicitDeclaration()
    {
        return className;
    }
}

I'm guessing whatever your real scenario is might make it impossible to apply this as a workaround though.