泛型上的InvalidCastException

Yan*_*hon 4 .net c# generics

来自Java世界,使用泛型和C#进行编程通常是一个令人头疼的问题.像这个:

interface ISomeObject { }
class SomeObjectA : ISomeObject { }
class SomeObjectB : ISomeObject { }


interface ISomething<T> where T : ISomeObject
{
    T GetObject();
}
class SomethingA : ISomething<SomeObjectA>
{
    public SomeObjectA GetObject() { return new SomeObjectA(); }
}
class SomethingB : ISomething<SomeObjectB>
{
    public SomeObjectB GetObject() { return new SomeObjectB(); }
}


class SomeContainer
{

    private ISomething<ISomeObject> Something;

    public void SetSomething<T>(ISomething<T> s) where T : ISomeObject
    {
        Something = (ISomething<ISomeObject>)s;
    }
}


class TestContainerSomething
{
    static public void Test()
    {
        SomeContainer Container = new SomeContainer();
        Container.SetSomething<SomeObjectA>(new SomethingA());
    }
}
Run Code Online (Sandbox Code Playgroud)

这将导致到InvalidCastExceptionSomething = (ISomething<ISomeObject>)s;.在Java中,这可以工作,我甚至可以使用(如果所有其他方法都失败)泛型通配符<?>.这在C#中是不可能的.

虽然这只是我用来解释问题的一个例子,但如何消除这种异常呢?唯一的主要限制是SomeContainer 不能成为泛型类

**注意**:关于这个问题有很多问题,但是没有一个(我能找到)解决非泛型类中的泛型类成员.

**更新**

在方法内部SetSomething,我添加了以下行:

Console.WriteLine(s.GetType().IsSubclassOf(typeof(ISomething<SomeObjectA>)));
Console.WriteLine(s.GetType().ToString() + " : " + s.GetType().BaseType.ToString());
foreach (var i in s.GetType().GetInterfaces())
{
    Console.WriteLine(i.ToString());
}
Run Code Online (Sandbox Code Playgroud)

令我惊讶的输出

False
SomeThingA : System.Object
ISomething`1[SomeObjectA]
Run Code Online (Sandbox Code Playgroud)

这是我得到这个例外的原因吗?

Ars*_*yan 5

Out关键字将是一个修复,如果你ISomething只有返回T的方法

interface ISomething<out T> where T : ISomeObject
Run Code Online (Sandbox Code Playgroud)

在创建通用接口时,您可以指定在具有不同类型参数的接口实例之间是否存在隐式转换.

它被称为协方差和逆变

Eric Lippert有一系列很好的文章,为什么我们需要考虑这个问题,这里使用了界面差异

这是我的代码,它对我来说是预期的

interface ISomeObject { }
class SomeObjectA : ISomeObject { }
class SomeObjectB : ISomeObject { }


interface ISomething<out T> where T : ISomeObject
{
    T GetObject();
}
class SomethingA : ISomething<SomeObjectA>
{
    public SomeObjectA GetObject() { return new SomeObjectA(); }
}
class SomethingB : ISomething<SomeObjectB>
{
    public SomeObjectB GetObject() { return new SomeObjectB(); }
}


class SomeContainer
{

    private ISomething<ISomeObject> Something;

    public void SetSomething<T>(ISomething<T> s) where T : ISomeObject
    {
        Something = (ISomething<ISomeObject>)s;
    }
}


class TestContainerSomething
{
    static public void Test()
    {
        SomeContainer Container = new SomeContainer();
        Container.SetSomething<SomeObjectA>(new SomethingA());
    }
}
Run Code Online (Sandbox Code Playgroud)