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