C#:避免if(x为Foo){…} else if(x is Bar){…}用于数据结构
我有一系列数据结构,如:
abstract class Base {...} class Foo : Base {...} class Bar : Base {...}
以及一个接受Base并根据它的子类转换它的方法:
void Convert(Base b) { if (b is Foo) // Do the Foo conversion else if (b is Bar) // Do the Bar conversion ...
显然这是一个糟糕的面向对象 – 转换方法必须知道Base的每个派生类,并且必须在每次扩展Base时进行更改。 解决这个问题的“正常”OO方法是使每个派生类的Base负责转换自身,例如
abstract class Base { abstract Converted Convert(); ...} class Foo : Base { override Converted Convert(){...} ...} class Bar : Base { override Converted Convert(){...} ...}
但是,在我编写的代码中,Base是一个纯数据结构(只有getter和setter – 没有逻辑),而且它在另一个我无权更改的程序集中。 有没有一种方法可以更好地构造代码,而不强制Base的派生类具有逻辑?
谢谢
如果你这样做会疼,那就不要那样做了。 根本问题是:
我有一个方法,需要一个
Base
……
由于这是根本问题,请将其删除。 完全摆脱那种方法,因为它很糟糕。
替换为:
void Convert(Foo foo) { // Do the Foo conversion } void Convert(Bar bar) { // Do the Bar conversion }
现在没有方法可以说谎,并说它可以转换任何 Base
,而事实上它不能。
我遇到了类似的情况,并提出了一个类似于函数式语言模式匹配的解决方案。 这是语法:
请注意,在lambdas中,foo和bar被强类型化为各自的类型; 不需要施放。
var convertedValue = new TypeSwitch (r) .ForType(foo => /* do foo conversion */) .ForType(bar => /* do bar conversion */) ).GetValue();
这是TypeSwitch类的实现:
public class TypeSwitch { bool matched; T value; TResult result; public TypeSwitch(T value) { this.value = value; } public TypeSwitch ForType(Func caseFunc) where TSpecific : T { if (value is TSpecific) { matched = true; result = caseFunc((TSpecific)value); } return this; } public TResult GetValue() { if (!matched) { throw new InvalidCastException("No case matched"); } return result; } }
我确信它可以清理,在大多数情况下,Eric Lippert说这个前提存在根本缺陷是正确的。 但是,如果你碰到一个案例,你唯一的另一个选择是一堆is
和演员,我认为这是一个更清洁!