implict type cast in generic method

后端 未结 7 1225
刺人心
刺人心 2021-01-19 09:47

why do I get a compiler error in the following code stating: Cannot implicty convert type SpecialNode to T even though T must derive from NodeBase as I defined

相关标签:
7条回答
  • 2021-01-19 10:13

    The problem is that the function may be called with a type parameter T, which derives from NodeBase but not from SpecialNode. The compiler doesn't check the semantics of the if statement, so it doesn't know, that T has to be a specialNode.

    You would need to use an explicit cast to T in order to satisfy your compiler.

    0 讨论(0)
  • 2021-01-19 10:15

    The compiler doesn't read your if check to realize that in this particular line, T must be SpecialNode.

    You need to cast to NodeBase first, like this:

    return (T)(NodeBase)MySpecialNode;
    

    You need to casts because (as far as the compiler knows) T might be MyOtherSpecialNode, and you cannot cast a MyOtherSpecialNode to MySpecialNode.

    EDIT: You can do it with a single cast like this:

    NodeBase retVal;
    
    if (typeof(T) == typeof(SpecialNode))
        retVal = MySpecialNode;
    else if (typeof(T) == typeof(OtherSpecialNode))
        retVal = MyOtherSpecialNode;
    
    return (T)retVal;
    
    0 讨论(0)
  • 2021-01-19 10:16

    See my answer from a previous post

    Using Generics to return a literal string or from Dictionary<string, object>

    but the answer is

    return (T)MySpecialNode
    

    Because even you do the if check the compiler does not so you have to recast to T

    0 讨论(0)
  • 2021-01-19 10:18

    You might see that a condition has been logically met, but the compiler does not "remember" this.

    public static T GetNode<T>() where T : NodeBase
    {
        if (typeof(T) == typeof(SpecialNode)) // OK, you now know T is SpecialNode
        {
            // the compiler still insists on returning a T,
            // and will not assume that MySpecialNode is a T
            return MySpecialNode;
        }
    
        // ...
    
        return default(T);
    }
    

    It's true what others have already said: you must cast MySpecialNode: (T)(NodeBase)MySpecialNode (which you can do safely, because you have already checked that T is SpecialNode).

    It's easy to think of this as a shortcoming of the compiler; but this is just a mistake stemming from how obvious it seems that MySpecialNode is a T. Suppose I had a method like this:

    public T Get<T>() {
        if (typeof(T).FullName.Equals("System.Int32"))
            return 5;
        else
            return default(T);
    }
    

    Should this compile? I should hope not; the compiler needs to guarantee that it's returning an object of type T, and it can't be sure that 5 will meet that requirement just from some bizarre check that I the developer have performed. (Yes, I know that T is int, but I would not expect any reasonable compiler to determine that from a comparison of the System.Type.FullName property.)

    Checking if (typeof(T) == typeof(SpecialNode)) is really not so different from that.

    0 讨论(0)
  • 2021-01-19 10:18

    Ah, that moment you wish the language had some kinda static polymorphism. Type checking in a generic method is not very cool. This can be a better solution if your requirements let:

    public abstract class NodeBase
    {
        public abstract NodeBase GetNode();
    }
    
    public class SpecialNode : NodeBase
    {
        public override NodeBase GetNode()
        {
            return ThisStaticClass.MySpecialNode;
        }
    }
    
    public class OtherSpecialNode : NodeBase
    {
        public override NodeBase GetNode()
        {
            return ThisStaticClass.MyOtherSpecialNode;
        }
    }
    
    //and in your generic method, just:
    public static T GetNode<T>() where T : NodeBase, new()
    {
        return (T)new T().GetNode();
    }
    

    This suffer from a disadvantage that you have to expose a parameterless constructor. In case that's undesirable, may be a slightly better approach is to push the generic call one layer backward, and ask your static class itself to do the work for you. Changing static class definition to something like:

    public static T GetNode<T>() where T : NodeBase
    {
        return ThisStaticClass<T>.GetNode();
    }
    
    //in which case ThisStaticClass<T> have awareness of 
    //parameterless new() constructor of T class, which still need not be good enough
    

    You can get it a bit more strongly typed by going one generic level deeper.

    public abstract class NodeBase<T> where T : NodeBase<T>
    {
        public abstract T GetNode();
    }
    
    public class SpecialNode : NodeBase<SpecialNode>
    {
        public override SpecialNode GetNode()
        {
            return ThisStaticClass.MySpecialNode;
        }
    }
    
    public class OtherSpecialNode : NodeBase<OtherSpecialNode>
    {
        public override OtherSpecialNode GetNode()
        {
            return ThisStaticClass.MyOtherSpecialNode;
        }
    }
    
    //avoids cast
    public static T GetNode<T>() where T : NodeBase<T>, new()
    {
        return new T().GetNode();
    }
    
    0 讨论(0)
  • 2021-01-19 10:23

    You could also do:

    public static T GetNode<T>() where T : NodeBase
    {
        T result;
    
        result = ThisStaticClass.MySpecialNode as T;
        if (result != null) return result;
    
        result = ThisStaticClass.MyOtherSpecialNode as T;
        if (result != null) return result;
    
        return default(T);
    }
    

    Edit: If the static classes are already null, this probably wouldn't have the intended effect.

    0 讨论(0)
提交回复
热议问题