如何处理 C# 中缺少多重继承的问题

本文关键字:多重继承 问题 何处理 处理 | 更新日期: 2023-09-27 18:36:55

我正在为"可运行"的东西开发一个迷你框架。(它们是实验、测试、任务等)

// Something that "runs" (in some coordinated way) multiple "runnable" things.
interface IRunnableOf<T> where : IRunnable
// Provide base-class functionality for a "runner"
abstract class RunnerBase<T> : IRunnableOf<T>

class SequentialRunner<T> : RunnerBase<T>  // Same interface, different behavior.
class ConcurrentRunner<T> : RunnerBase<T>
// other types of runners.
class ConcurrentBlockRunner : SequentialRunner<Block>
class SequentialBlockRunner : ConcurrentRunner<Block>

现在,如何协调ConcurrentBlockRunnerSequentialBlockRunner? 我的意思是:

  1. 按共同祖先引用它们,以便在集合中使用。( IEnuerable<T> 其中 T = ??

  2. 提供其他基类功能。(例如,添加属性)。


我通过添加另一个接口来补救 #1,该接口刚刚将类型参数指定为 IA<T>

interface IBlockRunner : IRunnableOf<Block> { }

并将我的ConcurrentBlockRunnerSequentialBlockRunner定义修改为:

class ConcurrentBlockRunner : SequentialRunner<Block>, IBlockRunner
class SequentialBlockRunner : ConcurrentRunner<Block>, IBlockRunner

由于 ConcurrentBlockRunnerSequentialBlockRunner 都使用 Block 作为其类型参数,因此这似乎是一个正确的解决方案。 但是,我不禁对此感到"奇怪",因为好吧,我只是添加了该界面。


对于#2,我想向ConcurrentBlockRunnerSequentialBlockRunner添加几条通用数据。有几个属性适用于它们,但不适用于它们唯一的公共基类,该基类一直处于RunnerBase<T>

这是我第一次在使用 C# 时觉得多重继承会有所帮助。如果我能做到:

abstract class BlockRunnerBase {
   int Prop1 { get; set; }
   int Prop2 { get; set; }
class ConcurrentBlockRunner : SequentialRunner<Block>, BlockRunnerBase
class SequentialBlockRunner : ConcurrentRunner<Block>, 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 来代理事件,而是实际处理它们,并引发我自己的事件。仅仅为了添加几个属性就做了这么多工作......

如何处理 C# 中缺少多重继承的问题

Composition over Inheritance, FTW!

更明确地说:

class SequentialRunner<T> : RunnerBase<T>

应该实现IRunnableOf<T>并代理RunnerBase<T>而不继承它。

class SequentialRunner<T> : IRunnableOf<T>
{
   private readonly RunnerBase<T> _runnerBase;
   ...
}

您可以使用扩展方法来创建类似 mixin 的构造,即使使用类似属性的元素也是如此。

我还在 C# 中创建了一个类似特征的构造实验,NRoles。

但是,所有这些都需要非标准编码,对于旨在向第三方公开的 API 来说并不理想。我认为您应该尝试重新排列您的类,并在可能的情况下使用接口进行委托。