我有一个奇怪的设计情况,我以前从未遇到过......如果我使用Objective-C,我会用类别解决它,但我必须使用C#2.0.
首先,一些背景.我在这个类库中有两个抽象层.底层为扫描内容的组件实现了插件架构(抱歉,不能比这更具体).每个插件都会以一种独特的方式进行扫描,但插件也可能因其接受的内容类型而异.由于与本讨论无关的各种原因,我不想通过插件界面公开泛型.因此,我最终为每种内容类型提供了一个IScanner接口和一个派生接口.
顶层是一个便利包装器,它接受包含各种部分的复合内容格式.不同的扫描程序将需要复合的不同部分,具体取决于它们感兴趣的内容类型.因此,我需要具有特定于每个IScanner派生接口的逻辑,该接口解析复合内容,查找所需的相关部分.
解决此问题的一种方法是简单地向IScanner添加另一种方法并在每个插件中实现它.但是,双层设计的重点在于插件本身不需要了解复合格式.解决这个问题的蛮力方法是在上层进行类型测试和向下转换,但是需要谨慎维护这些方法,因为将来会添加对新内容类型的支持.在这种情况下,访客模式也很尴尬,因为实际上只有一个访问者,但不同的可访问类型的数量只会随着时间而增加(即 - 这些是访问者适合的相反条件).而且,当我真正想要的只是劫持IScanner的单一发送时,双重发送感觉就像矫枉过正!
如果我使用的是Objective-C,我只需在每个IScanner派生的接口上定义一个类别,并在那里添加parseContent方法.该类别将在上层定义,因此插件不需要更改,同时避免了类型测试的需要.不幸的是,C#扩展方法不起作用,因为它们基本上是静态的(即 - 与调用站点使用的引用的编译时类型相关联,而不是像Obj-C类别那样挂钩到动态调度).更不用说,我必须使用C#2.0,因此我甚至无法使用扩展方法.:-P
那么在C#中是否有一种简洁的方法来解决这个问题,类似于如何用Objective-C类别解决它?
编辑:一些伪代码,以帮助使当前设计的结构清晰:
interface IScanner { // Nothing to see here... } interface IContentTypeAScanner : IScanner { void ScanTypeA(TypeA content); } interface IContentTypeBScanner : IScanner { void ScanTypeB(TypeB content); } class CompositeScanner { private readonly IScanner realScanner; // C-tor omitted for brevity... It takes an IScanner that was created // from an assembly-qualified type name using dynamic type loading. // NOTE: Composite is defined outside my code and completely outside my control. public void ScanComposite(Composite c) { // Solution I would like (imaginary syntax borrowed from Obj-C): // [realScanner parseAndScanContentFrom: c]; // where parseAndScanContentFrom: is defined in a category for each // interface derived from IScanner. // Solution I am stuck with for now: if (realScanner is IContentTypeAScanner) { (realScanner as IContentTypeAScanner).ScanTypeA(this.parseTypeA(c)); } else if (realScanner is IContentTypeBScanner) { (realScanner as IContentTypeBScanner).ScanTypeB(this.parseTypeB(c)); } else { throw new SomeKindOfException(); } } // Private parsing methods omitted for brevity... }
编辑:为了澄清,我已经考虑过这个设计了很多.我有很多原因,其中大多数是我无法分享的,原因就在于它的原因.我还没有接受任何答案,因为虽然有趣,但他们回避了原来的问题.
事实是,在Obj-C中,我可以简单而优雅地解决这个问题.问题是,我可以在C#中使用相同的技术吗?如果是,如何使用?我不介意寻找替代方案,但公平地说这不是我问的问题.:)