1

我可以访问一个无法修改的类结构,如下所示:

Graphics
  Circle
  Line
  etc.

再说一次,我不能修改它!这些都具有单独的属性,例如RadiusFirstPointLastPoint等,以及一些共同的属性。

我想创建一个接受Graphics对象的方法,并且根据对象的类型,将运行一个ToJson方法:

Graphics g = db.GetGraphic(123);
// Console.WriteLine(g.GetType()) prints "Circle"

// Should run some specific implementation for `Circle` type graphics, and
// have an overload for all types including Graphics
ToJson(g);

起初我以为我可以巧妙地重载该ToJson方法:

ToJson(Graphics g) { ... }
ToJson(Circle g) { ... }
ToJson(Line g) { ... }

但是,这当然适用于ToJson(Graphics)每次的通用重载。

我确定我可以执行以下操作:

if (g is Circle) ...
if (g is Line) ...
if (g is Graphics) ...

或创建一个字典来降低每种类型的复杂性,但这并不是最好的做事方式


我考虑过的

我已经考虑过是否可以在每个对象周围使用一些通用的包装方法(例如,new JsonGraphics(g).ToJson()),但我不想自己执行任何手动类型检查。

我已经查看了双重调度和访问者模式,但我不确定它们是否满足我的要求,因为它们看起来我必须修改这些类(或者我可能只是没有完全理解它们),并且(有点明显,但是) 泛型也基本上不在窗口中,因为它们需要我提前知道Graphics这是什么类型的对象。


那么两个问题:

除了使用某些 Dictionary 或其他类似的东西之外,还有更好的方法if (g is Type)吗?

如果我可以修改类,模式会怎样?在这种情况下这是不可能的,但是在我可以的情况下,双重调度/访客是最好的方式吗?

4

1 回答 1

2

由于无法修改基类,或者在具体类型变成泛型Graphics类型之前无法访问它,不幸的是,我认为除了检查对象的运行时类型之外,您无能为力Graphics

您可以使用 switch 语句(自 C# 7.0 起),它比您的if链稍微干净:

switch (g)
{
    case Circle circle: ... break;
    case Line line: ... break;
    default: /* Oh no! */ break;
}

就我个人而言,我认为在这样的 switch 语句中使用 Dictionary 并没有太大的优势——两者都可以放入一个独立的方法中(从而减少违反打开/关闭原则的数量),但是 switch会便宜很多。

您还可以使用dynamic,这会导致运行时进行后期绑定:

dynamic d = g;
ToJson(d); // Picks the right ToJson overload corresponding to the runtime type of 'd'

...虽然,动态具有相当大的运行时成本,并且通常被认为是气味。

于 2019-03-27T14:47:25.010 回答