简体   繁体   中英

Unable to cast MyType<T> to MyType<object>

The specific exception I'm getting is:

Unable to cast object of type NbtByte to type INbtTag<System.Object>

On this line:

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

Where tag is declared as:

INbtTag<object> tag;

And NbtByte is defined as:

public class NbtByte : INbtTag<byte>

Where IBtTag is:

public interface INbtTag<out T>

I thought by declaring it as out T I would be able to do things like this.

Basically, I want to have a dictionary of IbtTag<T> s,

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

but where T is of different types (thus I declared it with object ). Is this possible?

Interface variance applies only to reference types. Value types (such as ints, bytes, etc., as well as custom structs) are excluded. For example, you cannot use an array of integers as IEnumerable<object> even though the array is IEnumerable<int> .

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

To solve your issue with the dictionary, you can perhaps elect to define a non-generic interface. (Where your generic interface might expose members as type T, the non-generic interface would simply expose object .)

Say you have

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

Then you could use your dictionary as Dictionary<string, INbtTag> .

The drawback is that when you implement the interface, you have to implement both. This usually means implementing the generic version implicitly, and the non-generic explicitly. For example:

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
    }
}

One of the limiting aspects of using generic types is that generic types are not as flexible in type conversions as traditional types. A List<Object> is not assignment compatible with a List<String> or any other object type.

There are conversion helper functions in Linq such as .Cast<T>() that will logically cast every element from one list to the T type to form a new list of type List<T> . While the helper function is handy, it doesn't change the fact that List<N> is not type compatible with List<T> even if N and T are type compatible in some fashion.

Essentially, generic types aren't polymorphic. There is no common type between instances of the same generic type - no type you can declare a variable with that can hold a List<T> and a List<N> .

If you are creating your own generic type and you do want to have some common type that can be used to carry around all manifestations of the generic type, you have to perform some type gymnastics to get it to work, and you will be limited in what you can do. You need to define a base class or interface type, and then you need to make your generic type inherit from the base class or implement the interface type. With this configuration, you can declare a variable with the base class type or interface type, and can assign MyClass<N> and MyClass<T> to the same variable, accessing only the members defined in the base class (which will of course not have any knowledge of the N or T type params).

Covariant type params ( IMyInterface<out T> ) may get you part of the way there but covariance places severe restrictions on what you can do in that interface - the covariant type T can only be used for function results, never for arguments or settable properties.

The simple fact that IList<T> is not declared covariant is not an accident or oversight - it can't be covariant. To be useful IList<T> needs methods like Add(T item) and Remove(T item) which are forbidden when T is covariant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM