7

I have the following short C# program:

IList<string> listString = new List<String>();
IList<object> listObject;

listObject = listString;

This program doesn't compile. The last line causes the following compilation error:

Cannot implicitly convert type 'System.Collections.Generic.IList' to 'System.Collections.Generic.IList'. An explicit conversion exists (are you missing a cast?)

So, I've added the cast:

listObject = (IList<object>)listString;

Now the program compiles properly, but fails at runtime. An InvalidCastException is raised with the following message:

Unable to cast object of type 'System.Collections.Generic.List'1[System.String]' to type 'System.Collections.Generic.IList'1[System.Object]'.

Either the cast is illegal and should be caught by the compiler, or it is legal and shouldn't throw an exception at runtime. Why the inconsistent behavior?

CLARIFICATION: I am not asking why the cast fails. I understand why such casting is problematic. I am asking why the cast fails only at runtime.


You can do this by Collection Initializer. The below code snippets works for me.

IList<string> listString = new List<String>();
IList<object> listObject;

listObject = new List<object>(listString);
4

5 回答 5

11

The reason why the implicit cast from IList<string> to IList<object> won't compile is, as you seem to know, that the IList<T> interface is not covariant in T. If, with .NET 4.5, you used IReadOnlyList<out T> instead, it would work.

The reason why the explicit cast

listObject = (IList<object>)listString;

will compile, is not that IList<string> and IList<object> are related in any way. Neither type is assignable to the other. The reason is that the runtime type of your variable (listString) might be a class (or struct) which implemented both interfaces! Suppose I made this class:

class CrazyList : IList<string>, IList<object>  // not very smart
{
  // a lot of explicit interface implementations here
}

Then if some IList<string> happened to be a CrazyList at runtime, the explicit cast would succeed. When you write an explicit cast, you're telling the compiler "I know the type is going to be convertible to this type I'm casting into". Since the compiler can't prove you're wrong, of course it believes you.

于 2013-07-08T08:57:29.017 回答
4

One way to come up with list of strings to a list of objects would be:

IList<object> objects = listOfStrings.Cast<Object>().ToList();

Please note it would be best for the maintainability of your code to limit such casts, and be very explicit about them when they need to happen, marking them with a comment if the reason behind the conversion is not immediately evident.

EDIT: An important note is that the code above does not actually cast one type of list to another. Rather, it casts individual members of the initial list to another type, then the .ToList() method creates a separate list with the converted objects.

SECOND EDIT: Actually, none of the commented questions explains the problem adequately. Please check John Skeet 's answer here. An explicit cast will not fail at compile time, since the compiler expects that it will encounter the explicit cast 's implementation at some point, while a covariant assignment using a generic IList<T> interface will fail, since it 's not supported.

于 2013-07-08T08:17:09.073 回答
2

listObject.Add(10); // ok
string s = listString[0]; // WTF?!!!

Due to IList mutability such conversion is meaningless.

于 2013-07-08T08:16:41.150 回答
1

If you can't access the underlying List,

IList<object> listObject = listString.Cast<object>().ToList();

If you can access the underlying List,

Ilist<object> listobject = (new List<string>()).ConvertAll(s => (object)s);
于 2013-07-08T08:22:53.457 回答
1

您可以通过 Collection Initializer 来做到这一点。下面的代码片段对我有用。

IList<string> listString = new List<String>();
IList<object> listObject;

listObject = new List<object>(listString);
于 2013-07-08T08:16:11.287 回答