问题
Yesterday, I was explaining C#'s generic constraints to my friends. When demonstrating the where T : CLASSNAME
constraint, I whipped up something like this:
public class UnusableClass<T> where T : UnusableClass<T>
{
public static int method(T input){
return 0;
}
}
And was really surprised to see it compile. After a bit of thinking, however, I figured it was perfectly legal from the point of view of the compiler - UnusableClass<T>
is as much of a class as any other that can be used in this constraint.
However, that leaves a couple of questions: how can this class ever be used? Is it possible to
- Instantiate it?
- Inherit from it?
- Call its static method
int method
?
And, if yes, how?
If any of these is possible, what would the type of T
be?
回答1:
Well.
public class Implementation : UnusableClass<Implementation>
{
}
is perfectly valid, and as such makes
var unusable = new UnusableClass<Implementation>();
and
UnusableClass<Implementation>.method(new Implementation());
valid.
So, yes, it can be instantiated by supplying an inheriting type as the type parameter, and similarly with the call to the static method. It's for instance useful for tree-like structures where you want to generically specify the type of children the node has, while it being the same type itself.
回答2:
This approach is widely used in Trees and other Graph-like structures. Here you say to compiler, that T has API of UnusableClass. That said, you can implement TreeNode as follows:
public class TreeNode<T>
where T:TreeNode<T>
{
public T This { get { return this as T;} }
public T Parent { get; set; }
public List<T> Childrens { get; set; }
public virtual void AddChild(T child)
{
Childrens.Add(child);
child.Parent = This;
}
public virtual void SetParent(T parent)
{
parent.Childrens.Add(This);
Parent = parent;
}
}
And then use it like this:
public class BinaryTree:TreeNode<BinaryTree>
{
}
回答3:
If any of these is possible, what would the type of T be?
They are all possible, and you are the one who is gonna determine what is the type of T
.For example let's assume there is a type that inherits from UnusableClass<T>
class Foo : UnusableClass<Foo> { }
Now you can instantiate UnusableClass<Foo>
because Foo
satisfies the constraint:
UnusableClass<Foo> f = new UnusableClass<Foo>();
Then the type of T
become Foo
and if you try to call method
you need to pass an instance of Foo
.
来源:https://stackoverflow.com/questions/29228636/base-class-constraint-on-generic-class-specifying-the-class-itself