0

First off: I know there are a ton of "Cannot implicitly convert type" questions, but my question is more trying to wrap my head around why a specific combination of interfaces and generics does not compile. I've not been able to find an answer, so any pointer are welcome.

Basically, my problem is this:

I have a set of classes (base classes), all implementing a single interface. On top of this I want to build a "derived" interface that references the first set of classes set of classes. Finally, I have a static method, which takes an instance of a base class, and depending on the base class, returns an instance of the correct derived class.

Something like this (implemented in a .NET standard 2.0 class library):

    public static class GenericClass
    {
        public static IDerived<IBase> Create(IBase base)
        {
            //if typeof a is 
            return new DerivedImplementedA(){Value = (ImplementationA) base};
        }

    }

However, I cannot return a DerivedImplementedA class from a method with return signature IDerived<IBase>, although DerivedImplementedA is implemented as follows:

    public class DerivedImplementedA : IDerived<ImplementationA>
    {
        public ImplementationA Value { get; set; }
    }

In my head, since DerivedImplementedA implements IDerived with the generic parameter ImplementationAd, which in turn implements IBase, it would be perfectly fine to return DerivedImplementedA in cases where a IDerived<IBase> is expected. However, this is not the case. If I try the following

    public static class GenericClass
    {
        public static IDerived<IBase> Create(IBase a)
        {
            var temp = (ImplementationA) a;
            return (IDerived<IBase>) new DerivedImplementedA(){ Value = temp};
        }

    }

I get the following warning:

"Suspicious cast: there is no type in the solution which is inherited from both DerivedImplementedA and IDerived". I suspect the message is key, but as I said, in my head this works out just fine. What am I missing?

Or, how can I solve this issue in a way that actually compiles?

Complete example for completeness:

    public interface IBase
    {
        int A { get; set; }
    }

    public class ImplementationA : IBase
    {
        public int A { get; set; }
    }

    public interface IDerived<TBase>
    where TBase: IBase
    {
        TBase Value { get; set; }
    }

    public class DerivedImplementedA : IDerived<ImplementationA>
    {
        public ImplementationA Value { get; set; }
    }

    public static class GenericClass
    {
        public static IDerived<IBase> Create(IBase a)
        {
            var temp = (ImplementationA) a;
            return (IDerived<IBase>) new DerivedImplementedA(){ Value = temp};
        }

    }
atlefren
  • 210
  • 4
  • 10
  • I suspect you want to mark IDerived's type parameter as [covariant](https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance#defining-variant-generic-interfaces-and-delegates), i.e. `IDerived`, but I don't know this well enough. – Rup Feb 10 '20 at 13:16
  • 2
    _"I cannot return a DerivedImplementedA class from a method with return signature IDerived, although DerivedImplementedA is implemented as follows"_ That's because while `ImplementationA` may inherit `IBase`, `IDerived` _does not_ inherit `IDerived`. I think it's yet another case of covariance confusion. – Fildor Feb 10 '20 at 13:17
  • @Fildor Care to point me to somewhere where I can read up on "covariance confusion", why this applies here and what it means? – atlefren Feb 10 '20 at 14:47
  • 1
    [This answer](https://stackoverflow.com/a/2033921/2557128) shows why this doesn't work. – NetMage Feb 10 '20 at 23:04
  • 1
    Sorry for the late follow-up. Here is what Microsoft writes: [Covariance and Contravariance in Generics](https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance) and also [Covariance and Contravariance (C#)](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/) – Fildor Feb 14 '20 at 12:26

1 Answers1

1

Your initial intent is already wrong, your method can be broken just by passing another class implementing the same interface.

Plus, what you are trying to do is not possible because of covariance rules.

This is what you should do:

public static IDerived<ImplementationA> Create(ImplementationA a)
{
    return new DerivedImplementedA() { Value = a };
}
Marco Salerno
  • 5,131
  • 2
  • 12
  • 32