1

I have some classes as follows:

public class RowBase { }

public class SpecificRow : RowBase { }

public class RowListItem<TRow> where TRow : RowBase { }

public class SpecificRowListItem : RowListItem<SpecificRow> { }

And some methods as follows:

public string GetName<TRow>(RowBase row) where TRow : RowBase { }

public string GetName<TRow>(RowListItem<TRow> item) where TRow : RowBase { }

The problem I'm having is that a subclass of RowListItem fails to match the signature of the second overload. Here are examples:

var foo = new SpecificRow();
var bar = new SpecificRowListItem();
var baz = new RowListItem<SpecificRow>();
string name;
name = GetName(foo); // invokes first overload as expected
name = GetName(baz); // invokes second overload as expected
name = GetName(bar); // does not compile
name = GetName((RowListItem<SpecificRow>)bar); // this alternative invokes the second overload
name = GetName<SpecificRow>(bar); // this alternative also invokes the second overload

The compiler error is

Error CS0311 The type 'ConsoleApplication1.SpecificRowListItem' cannot be used as type parameter 'TRow' in the generic type or method 'Program.GetName(TRow)'. There is no implicit reference conversion from 'ConsoleApplication1.SpecificRowListItem' to 'ConsoleApplication1.RowBase'.

Since SpecificRowListItem is a subclass of RowListItem<TRow> with a TRow that meets the where TRow : RowBase constraint, I would expect that the compiler would be able to tell that it should match the second overload when provided a parameter as an instance of that class. However, the text of the compiler error indicates that it's attempting to match the first overload (GetName(TRow)). I'd like to understand why that is, and if there's anything I can do to solve the problem other than the two alternatives that are working. I tried this:

public string GetName<TItem, TRow>(TItem item)
    where TItem : RowListItem<TRow>
    where TRow : RowBase

Aside from being ugly, it gave me the same problem (appears to match the first overload).

itsme86
  • 19,266
  • 4
  • 41
  • 57
John Riehl
  • 1,270
  • 1
  • 11
  • 22

1 Answers1

1

RowListItem<SpecificRow> has no relation to RowListItem<BaseRow>, it does not derive from it.

check out co-variance in generics. this answer might help you: "out T" vs. "T" in Generics

Working example:

  using System;

namespace CoVariance
{
    public class RowBase { }

    public class SpecificRow : RowBase { }

    public class RowListItem<TRow> : IInputSave<TRow> where TRow : RowBase { }

    public class SpecificRowListItem : RowListItem<SpecificRow> { }

    internal interface IInputSave<out TRow>
        where TRow : RowBase
    {
    }

    class Program
    {
        public static void Main(string[] args){

            var foo = new SpecificRow();
            var bar = new SpecificRowListItem();
            var baz = new RowListItem<SpecificRow>();
            string name;

            name = GetName(foo);
            Console.WriteLine(name); //oink
            name = GetName(baz);
            Console.WriteLine(name); //nested oink
            name = GetName(bar);
            Console.WriteLine(name); //nested oink
            name = GetName((RowListItem<SpecificRow>)bar);
            Console.WriteLine(name); //nested oink
            //name = GetName<SpecificRow>(bar); 

            Console.ReadKey();
        }

        public static string GetName(RowBase row)
        {
            return "oink";
        }

        public static string GetName(IInputSave<RowBase> item)
        {
            return "nested oink";
        }
    }
}
Community
  • 1
  • 1
keydon
  • 729
  • 6
  • 8
  • Thanks, but I'm not following you. Based on how I understand your answer, `GetName((RowListItem)bar)` should also fail, since it doesn't derive from `RowListItem`. However, it succeeds because of the `where TRow : BaseRow` constraint, which matches any `TRow` that inherits from `BaseRow`. My question is whether there's a reason the compiler can't implicitly cast from `SpecificRowListItem` to `RowListItem` even though the first inherits from the second? – John Riehl Aug 31 '16 at 21:47
  • i added a working example to my answer based on your example for leveraging co-variance in c#. but i am not able to properly explain it to you, since i dont fully understand it myself xD but there is a lot to read about that stuff on the internet :d – keydon Aug 31 '16 at 23:18
  • I'm glad you don't understand it either--makes me feel like less of a doofus ;-) [This link](http://stackoverflow.com/a/9381751/4062628) was interesting as well. – John Riehl Sep 02 '16 at 13:37