泛型上的InvalidCastException

时间:2012-11-29 15:30:28

标签: c# generics .net

来自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());
    }
}

结果为InvalidCastException Something = (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());
}

令我惊讶的输出

False
SomeThingA : System.Object
ISomething`1[SomeObjectA]

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

2 个答案:

答案 0 :(得分:5)

如果您的ISomething只有返回T的方法,那么

Out关键字将是一个修正

interface ISomething<out T> where T : ISomeObject

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

它被称为Covariance and Contravariance

Eric Lippert有一个很好的series of articles为什么我们需要考虑这个问题,这里使用了interface variance

这是我的代码,它按我的预期工作

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());
    }
}

答案 1 :(得分:2)

有时让通用接口实现非通用接口来规避丢失的<?>

interface ISomething
{
    object GetObject();
}

interface ISomething<T> : ISomething
    where T : ISomeObject
{
    T GetObject();
}

public class SomeImplementation<T> : ISomething<T>
{
    public T GetObject()
    {
        ...
    }

    object ISomething.GetObject()
    {
        return this.GetObject(); // Calls non generic version
    }
}

然后可以使用非通用接口

键入集合
var list = new List<ISomething>();
list.Add(new SomeImplementation<string>());
list.Add(new SomeImplementation<int>());