0

I'm trying to understand some specifics about C#'s generics.

If I have a method defined as such

 public static void AssertContains<T>(IEquatable<T> val, List<IEquatable<T>> optionsObjs, XML xml, string context)

and a class that implements IEquatable,

public class Tag : IEquatable<Tag>
{
    public string id;
    public bool Equals(Tag other)
    {
        return other.id == this.id;
    }
}

why is the following invalid?

AssertContains(aTag, aListOfTags, el, "");
Jephron
  • 2,652
  • 1
  • 23
  • 34

5 Answers5

2

This happens because List<T> isn't covariant, so you cant convert List<Type> to List<BaseType>.

In your case List<Tag> and List<IEquatable<Tag>>.

You can fix this changing optionsObjs parameter type to IEnumerable<IEquatable<T>>:

public static void AssertContains<T>(IEquatable<T> val, 
                                     IEnumerable<IEquatable<T>> optionsObjs,
                                     XML xml,
                                     string context)

Thats because IEnumerable<T> is covariant, so you can do things like this:

IEnumerable<IEquatable<Tag>> list = new List<Tag>(); 
Arturo Menchaca
  • 15,783
  • 1
  • 29
  • 53
2

This question is asked in some form almost every day.

A list of animals may not be used in any context in which a list of giraffes is needed. Why? Because a list of animals might contain a tiger.

A list of giraffes may not be used in any context in which a list of animals is needed. Why? Because a list of animals might have a tiger inserted into it, and now you've inserted a tiger into what is actually a list of giraffes.

However, a sequence -- IEnumerable<T> -- of giraffes may be used as a sequence of animals. Why is this legal, when it is illegal to do so with lists? Because sequences have no method that allows you to add a tiger to a sequence of animals.

Do a search on this site or the internet for "covariance and contravariance in C#" and you will find lots of information on this topic.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
1

That doesn't work because of contravariance; basically, a List<Derived> cannot be treated as a List<Base>.

Mark Brackett
  • 84,552
  • 17
  • 108
  • 152
0

I figured out that defining the method as

public static void AssertContains<T>(T val, List<T> optionsObjs, XML xml, string context) where T: IEquatable<T>

makes AssertContains(aTag, aListOfTags, el, ""); valid.

However, if someone can explain why my original post was wrong, I'll accept their answer.

Jephron
  • 2,652
  • 1
  • 23
  • 34
-1

I think the problem is in List<IEquatable<T>> because it is generic in generic. While using the below statement you avoid that generic-in-generic problem.

public static void AssertContains<T>(T val, List<T> optionsObjs, XML xml, string context) where T: IEquatable<T>

In order to demonstrate you that below I wrote it using 2 generic types:

public static void AssertContains<T, Z>(IEquatable<T> val, List<Z> optionsObjs, XML xml, string context) where Z : IEquatable<T>

That works out the same way, but taking a careful look leads us to the above statement, because IEquatable<T> val may be replaced by Z val. So the 2 generic types are useful if you had a generic parameter:

public static void AssertContains<T, Z>(T anotherValue, IEquatable<T> val, List<Z> optionsObjs, XML xml, string context) where Z : IEquatable<T>