【问题标题】:InvalidCastException on Generics泛型上的 InvalidCastException
【发布时间】:2012-11-30 04:47:18
【问题描述】:

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

这会导致InvalidCastExceptionSomething = (ISomething&lt;ISomeObject&gt;)s;。在 Java 中,这会起作用,我什至可以使用(如果一切都失败了)泛型通配符 &lt;?&gt;。这在 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]

这就是我得到这个异常的原因吗?

【问题讨论】:

    标签: c# generics .net


    【解决方案1】:

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

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

    【讨论】:

    • 我正在尝试使用out,但它给了我一些关于无法从程序集中加载ISomething_IMpl'1' 的错误(无行):)`
    • 您使用的是哪个版本的 .net?我记得它在 .net 3+ 中可用,我在 .net 4 上并且它可以工作
    • 最新的,我有“.Net 4 Client Profile”并更改为“.Net 4”,但结果相同。作为记录,我只是将interface ISomething&lt;T&gt; where T : ISomeObject 更改为interface ISomething&lt;out T&gt;
    • 是的,我刚刚试过,它说我无法加载接口,因为它声明了一个协变类型的参数,它既不是接口也不是委托。有趣,不是吗?顺便说一句:我已经安装了所有更新,.Net 4、VS2010 Ultimate(合法)等等。
    • @YanickRochon,你在尝试和这里一样的界面吗?能否请您复制确切的错误消息,我不确定您为什么会收到该消息?你在一个项目中尝试这个吗?
    【解决方案2】:

    有时让泛型接口实现非泛型接口以规避缺少的&lt;?&gt; 是有用的

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

    【讨论】:

    • 这很有趣。我认为所有对象都继承自object。并且有两个同名的接口,一个是通用的,另一个不是……对我来说很奇怪。
    • 但是这里的问题是方法GetObject需要为每个具体类型有一个特定的实现,因此为什么是泛型,我需要为一些非泛型类设置一个具体的实现( ISomething 必须在 SomeContainer 内可互换)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多