86

It seems that a List object cannot be stored in a List variable in C#, and can't even be explicitly cast that way.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

results in Cannot implicitly convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>

And then...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

results in Cannot convert type System.Collections.Generic.List<string> to System.Collections.Generic.List<object>

Of course, you can do it by pulling everything out of the string list and putting it back in one at a time, but it is a rather convoluted solution.


I'm personally not very big a fan of such puzzle questions, I prefer actual programming exercises in interviews.

That said, first it would depend on if I can tell if they are broken or not from the floor I am dropping them at. I will presume I can.

I would go up to the second floor, drop the first marble. If it broke I would try the first floor. If that broke I would know it was no floor.

If the first didn't break, I would go to the 4th floor and drop from there. If that broke, I would go back down and get the other marble, then drop at the 3rd floor, breaking or not I would know which is the limit.

If neither broke, I would go get both, and do the same process, this time starting at the 6th floor.

This way, I can skip every other floor until I get a marble that breaks.

This would be optimized for if the marble breaks early... I suppose there is probably an optimal amount of floors I could skip to get the most for each skip... but then if one breaks, I would have to check each floor individually from the first floor above the last known floor... which of course would be a pain if I skipped too many floors (sorry, not going to figure out the optimal solution right now).

Ideally, I would want a whole bag of marbles, then I could use a binary search algorithm and divide the number of floors in half with each drop... but then, that wasn't the question, was it?

4

14 に答える 14

39

Think of it this way, if you were to do such a cast, and then add an object of type Foo to the list, the list of strings is no longer consistent. If you were to iterate the first reference, you would get a class cast exception because once you hit the Foo instance, the Foo could not be converted to string!

As a side note, I think it would be more significant whether or not you can do the reverse cast:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

I haven't used C# in a while, so I don't know if that is legal, but that sort of cast is actually (potentially) useful. In this case, you are going from a more general class (object) to a more specific class (string) that extends from the general one. In this way, if you add to the list of strings, you are not violating the list of objects.

Does anybody know or can test if such a cast is legal in C#?

于 2008-08-09T02:50:14.903 に答える
36

If you're using .NET 3.5 have a look at the Enumerable.Cast method. It's an extension method so you can call it directly on the List.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

It's not exactly what you asked for but should do the trick.

Edit: As noted by Zooba, you can then call ol.ToList() to get a List

于 2008-08-09T03:07:44.337 に答える
14

型パラメーターが異なるジェネリック型間でキャストすることはできません。特殊化されたジェネリック型は同じ継承ツリーの一部を形成しないため、無関係な型です。

NET 3.5 より前でこれを行うには:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Linq の使用:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
于 2008-08-10T11:54:37.673 に答える
11

The reason is that a generic class like List<> is, for most purposes, treated externally as a normal class. e.g. when you say List<string>() the compiler says ListString() (which contains strings). [Technical folk: this is an extremely plain-English-ified version of what's going on]

Consequently, obviously the compiler can't be smart enough to convert a ListString to a ListObject by casting the items of its internal collection.

That's why there's extension methods for IEnumerable like Convert() that allow you to easily supply conversion for the items stored inside a collection, which could be as simple as casting from one to another.

于 2008-08-09T02:38:02.883 に答える
6

This has a lot to do with covariance, e.g., generic types are considered as parameters, and if the parameters do not resolve properly to a more specific type then the operation fails. The implication of such is that you really cannot cast to a more general type like object. And as stated by Rex, the List object won't convert each object for you.

You might want to try the ff code instead:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

or:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol will (theoretically) copy all the contents of sl without problems.

于 2008-08-09T02:58:26.463 に答える
5

はい、できます。.NET 3.5 から:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
于 2008-10-18T20:26:02.400 に答える
3

Mike - I believe contravariance isn't allowed in C# either

See Generic type parameter variance in the CLR for some more info.

于 2008-08-09T02:53:18.237 に答える
2

これ(共変性)は実際にはC#4.0でサポートされると思います。 http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

于 2008-11-07T19:01:21.417 に答える
1

これは、実際には、「ol」リストバリアントに奇妙な「オブジェクト」を入れようとしないためです (List<object>許可されているようです) - コードがクラッシュするためです (リストは実際にはList<string>String 型のオブジェクトのみを受け入れるため)。 )。そのため、変数をより一般的な仕様にキャストすることはできません。

Java では逆で、ジェネリクスがなく、代わりにすべてが実行時のオブジェクトのリストであり、厳密に型指定されたと思われるリストに奇妙なオブジェクトを実際に詰め込むことができます。「具体化されたジェネリック」を検索して、Java の問題に関するより幅広い議論を参照してください...

于 2008-08-10T14:35:48.940 に答える
1

ジェネリックでのそのような共分散はサポートされていませんが、実際には配列でこれを行うことができます:

object[] a = new string[] {"spam", "eggs"};

C# は実行時チェックを実行して、たとえば を にint挿入できないようにしますa

于 2008-10-09T21:54:25.877 に答える
0

うーん、以前のコメントのおかげで、それを見つける方法が 2 つ見つかりました。最初のものは、要素の文字列リストを取得し、それを IEnumerable オブジェクト リストにキャストします。

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

2 つ目は IEnumerable オブジェクト型を避け、文字列をオブジェクト型にキャストし、同じ文で関数 "toList()" を使用するだけです。

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

私は2番目の方法がもっと好きです。これが役立つことを願っています。

于 2016-03-17T21:38:26.597 に答える
0

コンテンツを暗黙的にキャストできる任意の IList に対する、.NET 3.5 以前の別のソリューションを次に示します。

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(ズーバの例に基づく)

于 2009-06-08T16:34:54.520 に答える
0

私は持っています:

private List<Leerling> Leerlingen = new List<Leerling>();

最終的に私List<object> のために働いたのはこれでした:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Castそれをその型から取得したいIEnumerable型に変換し、次に型キャストしIEnemuerableますList<>

于 2009-10-15T09:41:08.887 に答える