2012-02-18 56 views
2

我得到具体的例外是:无法施展的MyType <T>到MYTYPE <object>

无法转换NbtByte类型的对象键入INbtTag<System.Object>

在此行中:

tag = (INbtTag<object>)new NbtByte(stream); 

其中tag被声明为:

INbtTag<object> tag; 

而且NbtByte被定义为:

public class NbtByte : INbtTag<byte> 

其中IBtTag是:

public interface INbtTag<out T> 

我想通过声明它作为out T我将能够做这样的事情。

基本上,我想要的IbtTag<T>秒的字典,

var dict = new Dictionary<string, INbtTag<object>>(); 

但是其中T是不同类型的(因此我object声明它)。这可能吗?

回答

7

接口方差仅适用于参考类型。值类型(如整数,字节等,以及自定义结构)不包括在内。例如,即使数组为IEnumerable<int>,也不能使用整数数组作为IEnumerable<object>

IEnumerable<object> objs = new int[] { 1, 2, 3 }; // illegal 
IEnumerable<object> objs = new string[] { "a", "b", "c" }; // legal 

要解决您的字典问题,您可以选择定义非通用接口。 (如果您的通用接口,就有可能暴露会员类型T,非通用接口只会暴露object。)

说你有

interface INbtTag { } // non-generic interface 
interface INbtTag<out T> : INbtTag { } // covariant generic interface 

然后,你可以用你的词典作为Dictionary<string, INbtTag>

缺点是,当你实现接口时,你必须实现两者。这通常意味着隐式地实现通用版本,并且明确地非泛型。例如:

interface INbtTag 
{ 
    object GetValue(); 
} 

interface INbtTag<out T> : INbtTag 
{ 
    T GetValue(); 
} 

class NbtByte : INbtTag<byte> 
{ 
    byte value; 

    public byte GetValue() // implicit implementation of generic interface 
    { 
     return value; 
    } 

    object INbtTag.GetValue() // explicit implementation of non-generic interface 
    { 
     return this.GetValue(); // delegates to method above 
    } 
} 
+0

这就是我最初的做法,但非泛型缺少我想要的“Value”属性。我如何将它暴露为'object',然后“覆盖”类型为'T'?也许使用'new'关键字? – mpen 2012-02-18 05:35:43

+0

查看实施示例的更新。 – 2012-02-18 05:47:24

+0

啊......我想这解释了为什么我一直使用IEnumerables的模式!我想你也需要在'T GetValue()'上使用'new'关键字。这不太好,但我想这是我们能做的最好的。谢谢你的帮助! – mpen 2012-02-18 05:53:59

1

使用泛型类型的一个限制方面是泛型类型在类型转换中不像传统类型那样灵活。 A List<Object>不与List<String>或任何其他对象类型分配兼容。

Linq中有转换帮助函数,如.Cast<T>(),它们将从逻辑上将每个元素从一个列表转换为T类型,形成一个新类型的列表List<T>。尽管辅助函数非常方便,但它并不会改变List<N>List<T>类型不兼容的事实,即使N和T以某种方式进行类型兼容。

基本上,泛型类型不是多态的。相同通用类型的实例之间没有通用类型 - 没有类型可以声明一个变量,该变量可以包含List<T>List<N>

如果你正在创建你自己的泛型类型,并且你想要有一些可以用来承载泛型类型的所有表现形式的通用类型,那么你必须执行一些类型的体操才能使其工作,而你在你能做的事情上会受到限制。您需要定义基类或接口类型,然后您需要使您的泛型类型从基类继承或实现接口类型。使用此配置,可以使用基类类型或接口类型声明变量,并且可以将MyClass<N>MyClass<T>分配给同一个变量,只访问在基类中定义的成员(当然,这些成员不具有N或T型参数)。

协变类型参数(IMyInterface<out T>)可能会帮助您找到其中的一部分,但协变性会严重限制您可以在该接口中执行的操作 - 协变类型T只能用于函数结果,绝不可用于参数或可设置的属性。

IList<T>未声明协变的简单事实不是偶然或疏忽 - 它不能是协变的。要有用IList<T>需要像T是协变时禁止添加(T项)和删除项(T项)的方法。

+0

我没有意识到协变增加了限制。对我来说,我还应该多读一些新概念。感谢您的详细解答。 – mpen 2012-02-18 06:08:34

相关问题