Следующая часть кода C # не компилируется:
public class A
{
public interface B { }
}
public class C
: A,
C.B // Error given here: The type name 'B' does not exist in the type 'C'.
{ }
public class D : C.B // Compiles without problems if we comment out 'C.B' above.
{ }
Такое поведение является правильным в соответствии с Спецификация C # 4.0 (параграф 10.1.4.1):
При определении значения прямой спецификации базового класса A для класса B, прямой базовый класс B временно считается объектом. Интуитивно это гарантирует, что значение спецификации базового класса не может рекурсивно зависеть от самого себя.
У меня вопрос: почему такое поведение запрещено?
У Intellisense нет проблем с этим, хотя я знаю, что это мало что говорит, после того, как я стал свидетелем сбоя Visual Studio, когда Intellisense пытается понять некоторая комбинация злых классов с вариативными дженериками.
Поиск в Интернете приведенной выше цитаты из спецификации ничего не дал, так что я предполагаю, что об этом еще нигде не упоминалось.
Почему меня это волнует? Я разработал следующий фрагмент кода:
// The next three classes should really be interfaces,
// but I'm going to override a method later on to prove my point.
// This is a container class, that does nothing except contain two classes.
public class IBagContainer
where Bag : IBagContainer.IBag
where Pointer : IBagContainer.IPointer
{
// This could be an interface for any type of collection.
public class IBag
{
// Insert some object, and return a pointer object to it.
// The pointer object could be used to speed up certain operations,
// so you don't have to search for the object again.
public virtual Pointer Insert(object o) { return null; }
}
// This is a pointer type that points somewhere insice an IBag.
public class IPointer
{
// Returns the Bag it belongs to.
public Bag GetSet() { return null; }
}
}
// This is another container class, that implements a specific type of IBag.
public class BinarySearchTreeContainer : IBagContainer
where Tree : BinarySearchTreeContainer.BinarySearchTree
where Node : BinarySearchTreeContainer.BinarySearchTreeNode
{
// This is your basic binary search tree.
public class BinarySearchTree : IBagContainer.IBag
{
// We can search for objects we've put in the tree.
public Node Search(object o) { return null; }
// See what I did here? Insert doesn't return a Pointer or IPointer,
// it returns a Node! Covariant return types!
public override Node Insert(object o) { return null; }
}
// A node in the binary tree. This is a basic example of an IPointer.
public class BinarySearchTreeNode : IBagContainer.IPointer
{
// Moar covariant return types!
public override Tree GetSet() { return null; }
// If we maintain next and prev pointers in every node,
// these operations are O(1). You can't expect every IBag
// to support these operations.
public Node GetNext() { return null; }
public Node GetPrev() { return null; }
}
}
Вот, мы достигли ковариантных возвращаемых типов! Однако есть одна маленькая деталь.
Попробуйте создать экземпляр BinarySearchTree. Для этого нам нужно указать BinarySearchTreeContainer.BinarySearchTree для некоторых подходящих классов Tree и Node. Для Tree мы хотели бы использовать BinarySearchTree, для которого нам нужно было бы указать BinarySearchTreeContainer.BinarySearchTree ... И мы застряли.
По сути, это любопытно повторяющийся шаблонный шаблон (CRTP). К сожалению, мы не можем исправить это, как в CRTP:
public class BinarySearchTreeContainer
: BinarySearchTreeContainer
{ }
public class IBagContainer
: IBagContainer
{ }
(...)
BinarySearchTreeContainer.BinarySearchTree tree
= new BinarySearchTreeContainer.BinarySearchTree();
tree.Search(null);
IBagContainer.IBag bag = tree; // No cast!
//bag.Search(null); // Invalid!
//BinarySearchTreeContainer.BinarySearchTreeNode node
// = bag.Insert(null); // Invalid!
И мы возвращаемся к моему первоначальному вопросу: два верхних определения класса не разрешены спецификацией C #. Если бы это определение класса было разрешено, мои бинарные деревья поиска можно было бы использовать. Прямо сейчас они просто компилируются: их нельзя использовать.