【问题标题】:Using 'dynamic' in C# to implement Visitor Pattern在 C# 中使用“动态”来实现访问者模式
【发布时间】:2013-04-25 22:29:36
【问题描述】:

我有一个应用程序,我在其中对一系列元素执行操作,并且操作的确切性质取决于正在操作的元素的类型。由于封装的原因,不适合由元素来实现操作;这意味着它不能是元素类型的虚拟方法,因此“标准”多态性不起作用。我提出了一个与此相关的previous question,并被告知这被称为访问者模式。

我以前总是使用基于对象类型的if/elseif 调度程序方法来实现这一点,然后调用适当的实现。然而,最近,我注意到使用 dynamic 关键字可以完成同样的事情,如下所示:

private void ReconcileTips()
{
    foreach (var step in _definition.Steps)
    {
        ReconcileTips((dynamic)step);
    }
}

private void ReconcileTips(IBulkDispenseDefinition bulkDispense)
{
    bulkDispense.TipType = ReconcileTip(bulkDispense.TipType);
}

private void ReconcileTips(ImportScreenDefinition importScreen)
{
    foreach (var usage in importScreen.ReagentUsages)
        usage.TipType = ReconcileTip(usage.TipType);
}

private void ReconcileTips(BuildScreenDefinition buildScreen)
{
    foreach (var function in buildScreen.Functions)
        function.TipType = ReconcileTip(function.TipType);
}

类似的模式可以用于与类结构平行的其他操作,例如为_definition.Steps 的每个元素创建视图模型。想法是编译器基本上将其转换为我之前编写的相同if/elseif 逻辑,从而节省了我的精力。所以,有几个问题:

  1. 动态调度有没有我没有考虑过的问题?我相信这相当于执行一系列if (x is TypeA) Do((TypeA)x) else...,但我可能错了。

  2. 这实际上是否比冗长的if/elseif 方法更清晰、更易于理解?

【问题讨论】:

  • 除非我弄错了,否则您的代码似乎没有任何问题。相反,您只是在查看其他程序员对特定代码段的感受。在那种情况下,也许Code Review Stack Exchange 更适合这个问题?
  • @Mark,那可能是一个更好的位置,我可以迁移它;我想检查的主要项目是(1)(是否有我没有考虑过的与使用动态相关的陷阱。)

标签: c# .net dynamic visitor-pattern


【解决方案1】:

动态调度有什么我没有考虑过的问题吗?我相信这相当于执行一系列 if (x is TypeA) Do((TypeA)x) else...,但我可能是错的。

主要的问题是如果一个类型在你的访问者模式中实现了多个接口——编译器可能会选择你想要的那个,但如果你使用if (x is TypeA)/ else if (x is TypeB) 逻辑,因为您可以控制检查发生的顺序。

这实际上是否比长 if/elseif 方法更清晰、更容易理解?

我个人是这么认为的。这提供了由运行时类型确定的非常干净、相当体面的调度,并且“正常工作”。难以击败简单、简短、干净的代码。只要确保(可能)处理由于传入的错误类型而导致运行时错误的情况。

【讨论】:

  • 很高兴你提到了接口问题,因为我实际上只是在编写一个具有“重叠”接口的案例并记住了这个答案。
【解决方案2】:

是的,我正在考虑这种方法,但决定不采用更传统的方法。我从一个接口派生出每个访问者,该接口具有我想要为其实现操作的每种类型的 Visit 方法。

如果您有许多不同的操作想要作为访问者实现,例如。保存和加载操作,您可能希望在将来添加更多;使用动态方法,如果您忘记为需要处理的类型之一实现操作,则不会出现编译错误。您只会发现程序在运行时崩溃和烧毁的时间。

我想在编译时确保所有可能的类型都实现了任何操作。

【讨论】:

  • 是的,编译时检查的要点;在添加新类型时,我偶尔会忘记实现其中一个访问者。
  • 您可以添加一个 catch all 方法,以防止它崩溃。在该方法中,您可以以适当的方式记录某些内容或崩溃..
猜你喜欢
  • 1970-01-01
  • 2023-03-28
  • 2010-09-07
  • 2015-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多