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.


I came up with the same problem and discovered that, while AndroidTestCase does not have a constructor taking a String parameter, it does have a setName(String name) method. By calling the setName method, you can add individual method to the test case. Using the code in your example, your test suite may look like:

import junit.framework.TestSuite;

public class ProjectTestSuite_SomeTests extends TestSuite {

    public static Test suite () {
        TestSuite suite = new TestSuite("ArithmeticsTests");
        ArithmeticsTests arithmeticsTests = new ArithmeticsTests();
        arithmeticsTests.setName("testAddNumbers");
        suite.addTest(arithmeticsTests);
        ArithmeticsTests2 arithmeticsTest2 = new ArithmeticsTests2();
        arithmeticsTest2.setName("testSubtractNumbers");
        suite.addTest(arithmeticsTest2);

        return suite;
    }
}
4

5 に答える 5

11

IList<string>from からto への暗黙のキャストがコンパイルされない理由IList<object>は、ご存じのように、 ではIList<T>インターフェイスが共変でないためTです。.NET 4.5 でIReadOnlyList<out T>代わりに使用した場合、それは機能します。

明示的なキャストの理由

listObject = (IList<object>)listString;

コンパイルIList<string>れますが、そうでIList<object>、何らかの形で関連しています。どちらのタイプも他方に割り当てることはできません。その理由は、変数 (listString) の実行時の型が、両方のインターフェイスを実装したクラス (または構造体) である可能性があるためです! このクラスを作成したとします。

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

次に、一部が実行時にIList<string>発生した場合CrazyList、明示的なキャスト成功します。明示的なキャストを記述すると、コンパイラに「型がキャスト先の型に変換可能になることがわかっています」と伝えられます。コンパイラはあなたが間違っていることを証明できないので、もちろんあなたを信じます。

于 2013-07-08T08:57:29.017 に答える