Question about generic function parameter
I'm just wondering why passing a System.Collections.Generic.List<string>
into this function test(ICollection<object> t)
will not work, why can't I pass it in like passing a string
into test2(object t)
?
Doesn't make开发者_Go百科 much sense to me!
Because ICollection
isn't an output only interface, it is not covariant.
Consider this code:
void test(ICollection<object> t)
{
t.Add(new TextBox());
}
List<string> lst;
test(lst);
What is supposed to happen when test
tries to stuff a TextBox
into a List<string>
?
The contract for ICollection<object>
is that any object
can be put in, and items coming out will always be of type object
. But List<string>
only meets half that contract.
Because in C# 3.0 and .NET 3.5 or prior List<T>
implements ICollection<T>
. That means that the generic type of both List and ICollection must be the same.
In this scenario List<sting>
cannot be assigned to List<object>
although string is derived from object. In the same way List<string>
cannot be assigned to ICollection<object>
.
In C# 4.0 and .NET 4.0 we have the concept of covariance and contra-variance that allow you to assign a List<string>
to a IEnumerable<object>
.
here is a piece of code that works in C# 4.0
public static void Test(IEnumerable<object> input) {
// do something
}
static void Main(string[] args) {
var input = new List<string>();
Test(input);
}
Method test()
expects the parameter value to be of a type that implements the interface ICollection<object>
. Make sure class List
does.
@Ben's explanation is great and yes @Gallahad suggested, there are useful covariant and contravariant interfaces implemented in .Net 4.0. These allow you to do something like what you were trying to do, and guarantee that the pitfall code in Ben's example can't be written with them.
Meanwhile, you can of course do something like
private static void PrintAll<T>(IEnumerable<T> list)
{
foreach (var item in list)
{
Console.WriteLine(item.ToString());
}
}
static void Main()
{
List<int> numbers = Enumerable.Range(1, 10).ToList();
PrintAll(numbers);
}
this is a good way to achieve covariance. Furthermore, you can restrict what PrintAll's generic argument to a convenient base class using where clause.
精彩评论