简体   繁体   中英

implict type cast in generic method

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 in the where clause and even though SpecialNode actually derived from NodeBase?

    public static T GetNode<T>() where T : NodeBase
    {
        if (typeof(T) == typeof(SpecialNode))
        {
            return ThisStaticClass.MySpecialNode; // <-- compiler error
        }
        if (typeof(T) == typeof(OtherSpecialNode))
        {
            return ThisStaticClass.MyOtherSpecialNode; // <-- compiler error
        }
        ...
        return default(T);
    }

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;

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.

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

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.

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.

Why not just do the following:

return (T)MySpecialNode;

What version of .NET?

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

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