Я работаю над мини-фреймворком для "запускаемых" вещей. (Это эксперименты, тесты, задачи и т. д.)
// Something that "runs" (in some coordinated way) multiple "runnable" things.
interface IRunnableOf where : IRunnable
// Provide base-class functionality for a "runner"
abstract class RunnerBase : IRunnableOf
class SequentialRunner : RunnerBase // Same interface, different behavior.
class ConcurrentRunner : RunnerBase
// other types of runners.
class ConcurrentBlockRunner : SequentialRunner
class SequentialBlockRunner : ConcurrentRunner
Теперь, как я могу согласовать ConcurrentBlockRunner
и SequentialBlockRunner
? Под этим я подразумеваю:
Ссылаться на них по общему предку для использования в коллекции. ( IEnuerable
где T = ??)
Предоставляет дополнительные функциональные возможности базового класса. (Добавить свойство, например).
Я исправил #1, добавив еще один интерфейс, который просто указал параметр типа в IA
:
interface IBlockRunner : IRunnableOf { }
И изменил мои ConcurrentBlockRunner
и SequentialBlockRunner
определения будут следующими:
class ConcurrentBlockRunner : SequentialRunner, IBlockRunner
class SequentialBlockRunner : ConcurrentRunner, IBlockRunner
Поскольку ConcurrentBlockRunner
и SequentialBlockRunner
используют Block
в качестве параметра своего типа, это кажется правильным решением. Тем не менее, я не могу не чувствовать себя «странно» по этому поводу, потому что я только что добавил этот интерфейс.
Для #2 я хочу добавить пару общих данных в ConcurrentBlockRunner
и SequentialBlockRunner
. Есть несколько свойств, которые применяются к ним, но не к их единственному общему базовому классу, который полностью находится в RunnerBase
.
Впервые при использовании C# я почувствовал, что множественное наследование может помочь.Если бы я мог сделать:
abstract class BlockRunnerBase {
int Prop1 { get; set; }
int Prop2 { get; set; }
class ConcurrentBlockRunner : SequentialRunner, BlockRunnerBase
class SequentialBlockRunner : ConcurrentRunner, BlockRunnerBase
Тогда я мог бы просто добавить эти дополнительные свойства в BlockRunnerBase, и все просто заработало бы. Есть ли способ лучше?
Я знаю, что мне немедленно порекомендуют рассмотреть композицию, с которой я начал работать:
class BlockRunner : IBlockRunner {
IBlockRunner _member;
int Prop1 { get; set; } // Wish I could put these in some base class
int Prop2 { get; set; }
// Lots of proxy calls, and proxy events into _member
void Method() { _member.Method(); }
event SomeEvent
{
add { _member.SomeEvent += value; }
remove { _member.SomeEvent -= value; }
}
}
Проблема, с которой я столкнулся (побудившая меня написать этот вопрос), заключалась в том, что как только вы сочиняете, вы теряете совместимость типов. В моем случае _member запускал событие, поэтому параметр sender
имел тип SequentialBlockRunner
. Однако обработчик события пытался привести его к типу BlockRunner
, что, конечно же, не удалось. Решение состоит не в том, чтобы использовать add
/ remove
для проксирования событий, а на самом деле обрабатывать их и вызывать собственное событие. Столько работы, чтобы добавить пару свойств...