6

Recently I found a very surprising behavior in c#. I had a method which takes IEnumerable<Object> as a parameter and i was passing IEnumerable<string> but it's not possible. While in c# everything can be upcast to Object than why this is not possible? It's totally confusing for me. Please someone clear me on this issue.


The technical term for this is that generics are invariant in C# 3.0 and earlier. From C#4.0 onward, the cast works.

What invariant means is that there is no relationship between two generic types just because their generic type parameters are related (i.e. are sub- or supertypes of each other).

In your example, there is no typing relationship between an IEnumerable<object> and an IEnumerable<string>, just because string is a subtype of object. They're just considered two completely unrelated types, like a string and an int (they still both are subtypes of object, but everything is)

There are a few workarounds and exceptions for this issue you've run into.

First, you can cast each string individually to object, if you're using .NET 3.0 you can do that using the Cast<T>() extension method. Otherwise, you can use a foreach and put the result into a new variable of the static type you want.

Second, arrays are an exception for reference type, i.e. passing in a string[] type to a method acccepting object[] types should work.

4

5 回答 5

11

这方面的技术术语是泛型在 C# 3.0 及更早版本中是不变的。从 C#4.0 开始,演员表工作。

不变的意思是两个泛型类型之间没有关系,仅仅因为它们的泛型类型参数是相关的(即彼此的子类型或超类型)。

IEnumerable<object>在您的示例中, an和 an之间没有类型关系IEnumerable<string>,只是因为 string 是对象的子类型。它们只是被认为是两种完全不相关的类型,例如字符串和 int(它们仍然都是对象的子类型,但一切都是)

对于您遇到的此问题,有一些解决方法和例外情况。

首先,您可以将每个字符串单独转换为对象,如果您使用的是 .NET 3.0,您可以使用Cast<T>()扩展方法来做到这一点。否则,您可以使用 foreach 并将结果放入您想要的静态类型的新变量中。

其次,数组是引用类型的一个例外,即将 string[] 类型传递给接受 object[] 类型的方法应该可以工作。

于 2009-03-04T07:56:00.453 回答
8

As others have pointed out, generics types are invariant. IEnumerable<T> could be co-variant but C# doesn't currently support specifying variants. C# 4.0 is expected to support variants so this might be supported in the future.

To work around this now you can using a the LINQ extension method Cast<object>(). Assuming you have a method called Foo that takes an IEnumerable<object>>. You can call it like this,

Foo(stringEnumerable.Cast<object>());
于 2009-03-04T08:29:59.170 回答
3

The easiest way to pass IEnumerable<string> to function requiring IEnumerable<object> is through converting function like this:

public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
    {
    foreach (T o in enumerable)
        yield return o;
    }

When C# 4 comes out, this won't be neccessary, because it will support covariance and contravariance.

于 2009-03-04T08:15:12.747 回答
0

You should be using

IEnumerable<T> 

if you want to pass in different types, then you can query T to find out what type it is.

于 2009-03-04T08:01:11.673 回答
0

A good way to think about this is to ask yourself "What would happen if you could do this?". Take the following example:

IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal

objects.Add(new Integer(5)); // what the...

We just added an integer to a list of strings. The compiler does this to preserve type safety.

于 2009-03-04T08:01:44.883 回答