Reputation: 2748
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, "");
Upvotes: 0
Views: 142
Reputation: 660159
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.
Upvotes: 2
Reputation: 9
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>
Upvotes: -1
Reputation: 15982
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>();
Upvotes: 2
Reputation: 85665
That doesn't work because of contravariance; basically, a List<Derived>
cannot be treated as a List<Base>
.
Upvotes: 1
Reputation: 2748
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.
Upvotes: 0