0

Let A, B : A, C<T>, and D : C<B> be classes. Shouldn't I be able to cast a D to a C<A>?

    public enum TheEnem
    {
        One,
        Two
    }

    /// <summary>
    /// out means 'contravariant'
    /// which means that if U : T then an ITheInterface<U> can be cast to an ITheInterface<T>
    /// </summary>
    public interface ITheInterface<out T> where T : IComparable
    {
        T First { get; }
        T Second { get; }
    }

    public class TheClass : ITheInterface<TheEnem>
    {
        public TheEnem First { get; set; }
        public TheEnem Second { get; set; }

        public TheClass(TheEnem first, TheEnem second)
        {
            First = first;
            Second = second;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // There is no proble casting an enum member to IComparable.
            TheEnem e = TheEnem.Two;
            IComparable ec = e;
            IComparable ecc = (IComparable)e;

            TheClass a = new TheClass(TheEnem.One, TheEnem.Two);

            // We can cast the outer type OK.
            ITheInterface<TheEnem> i = a; // This doesn't need the 'out' -- we're not changing T.

            // But not the iner one:
            //ITheInterface<IComparable> c = a; // Cannot implicitly convert type TheClass' to 'ITheInterface<System.IComparable>'. An explicit conversion exists (are you missing a cast?)

            // So let's cast explicitly:
            ITheInterface<IComparable> c = (ITheInterface<IComparable>)a;
            // Now we get an excpetion at runtime:
            // Unable to cast object of type 'TheClass' to type 'ITheInterface`1[System.IComparable]
        }
    }
Richard Barraclough
  • 2,625
  • 3
  • 36
  • 54
  • Enums are value types so you can't use the covariance of `ITheInterface` to convert a `TheClass` to `ITheInterface`. – Lee Apr 28 '20 at 12:46
  • From [Variance in Generic Interfaces](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/variance-in-generic-interfaces): "Variance in generic interfaces is supported for reference types only. Value types do not support variance. For example, `IEnumerable` cannot be implicitly converted to `IEnumerable`, because integers are represented by a value type." – Damien_The_Unbeliever Apr 28 '20 at 12:49

1 Answers1

0

It's because you are using it with an enum, which is a value type, and variance isn't supported for value types.

E.g., this works:

public class TheEnum : IComparable
{
  public static TheEnum One { get => new TheEnum(); }
  public static TheEnum Two { get => new TheEnum(); }
  public int CompareTo(object other) => 0;
}

/// <summary>
/// out means 'contravariant'
/// which means that if U : T then an ITheInterface<U> can be cast to an ITheInterface<T>
/// </summary>
public interface ITheInterface<out T> where T : IComparable
{
    T First { get; }
    T Second { get; }
}

public class TheClass : ITheInterface<TheEnum>
{
    public TheEnum First { get; set; }
    public TheEnum Second { get; set; }

    public TheClass(TheEnum first, TheEnum second)
    {
        First = first;
        Second = second;
    }
}

class Program
{
    static void Main(string[] args)
    {
        // There is no proble casting an enum member to IComparable.
        TheEnum e = TheEnum.Two;
        IComparable ec = e;
        IComparable ecc = (IComparable)e;

        TheClass a = new TheClass(TheEnum.One, TheEnum.Two);

        // We can cast the outer type OK.
        ITheInterface<TheEnum> i = a; // This doesn't need the 'out' -- we're not changing T.

        ITheInterface<IComparable> c = a;

        ITheInterface<IComparable> c1 = (ITheInterface<IComparable>)a;
    }
}