Я использую существующую библиотеку классов, которую не могу изменить (Годо, если это имеет значение), и у меня есть свои собственные классы, которые наследуются от классов в этой библиотеке.
Не все мои классы будут расширять один и тот же класс в библиотеке, но у них будет общий класс-предок в этой библиотеке.
Я хочу написать несколько служебных методов, которые принимают некоторые из моих классов и вызывают методы общего базового класса.
Достаточно легко добавить интерфейс для общих методов в мои классы, но тогда я не могу также «требовать», чтобы тип имел общего предка.
Например, у меня может быть следующая иерархия:
Node (API class)
|
+-Node2D (API class)
|
+-RigidBody2D (API class)
| |
| +-MyClassA : ICommon (My custom class and interface)
|
+-Area2D (API class)
|
+-MyClassB : ICommon (My custom class and interface)
Затем мне нужен метод, который принимает ICommon
, но требует, чтобы он также расширялся Node2D
В настоящее время единственный способ сделать это - использовать дженерики, например:
void T MyMethod<T>(T arg) where T : Node2D, ICommon
но это кажется очень громоздким и не работает, если нужно вернуть другой тип (который по-прежнему имеет ограничение: Node2D
и ICommon
.
Это также не работает, если я хочу использовать смешанный массив классов, например:
void T[] OtherMethod<T>(T[] arg) where T : Node2D, ICommon
потребует, чтобы массив принадлежал к одному и тому же классу — у меня не может быть массива, состоящего из MyClassA
и MyClassB
.
Есть ли лучший способ справиться с этим?
Используйте общий базовый класс
public abstract class Node2DCommon<T> : Node2D, ICommon where T : Node2D, ICommon
{
// Your implementation
}
вы можете использовать массив Node2D
и убедиться, что они реализуют интерфейс ICommon
.
public void ProcessNodes(Node2D[] nodes)
{
foreach (var node in nodes)
{
if (node is ICommon commonNode)
{
// Call your common methods here
commonNode.CommonMethod();
}
}
}
Фактически вы можете объявить общий интерфейс
interface ICommon<T> : ICommon {
public T Instance { get; }
}
и пусть MyClassA
и MyClassB
реализуют его поверх ICommon
.
public class MyClassA : Node2D, ICommon<Node2D> {
public Node2D Instance => this;
}
public class MyClassB : Node2D, ICommon<Node2D> {
public Node2D Instance => this;
}
таким образом вы сможете получить доступ к методам Node2D
из свойства Instance интерфейса.
тогда первый метод:
ICommon<Node2D> MyMethod(ICommon<Node2D> arg)
{
// arg can be MyClassA
arg.Instance.Node2dMethod(); // access to methods of Node2D
return new MyClassB(); // can return MyClassB
}
не совсем понял, что вы имеете в виду и как вы собираетесь использовать массивы, но это тот, который позволяет возвращать сочетание классов в качестве элементов интерфейса как для аргумента, так и для возвращаемого значения:
ICommon<Node2D>[] OtherMethod(ICommon<Node2D>[] arg) {
return new MyClassB[10];
}
Это выглядит разумным компромиссом в решении моей проблемы. Создание универсального интерфейса также решит проблему с массивами, как вы показываете.
Я не уверен, что это соответствует моим требованиям. Если я сделаю это, мне придется заставить свой класс расширять этот абстрактный базовый класс - что означает, что мой класс расширяет только Node2D и не будет расширением RigidBody2D?