12

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

我以前总是使用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方法更清洁、更容易理解吗?

4

2 回答 2

10

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

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

这实际上比长的 if/elseif 方法更干净、更容易理解吗?

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

于 2013-05-01T21:01:04.593 回答
5

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

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

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

于 2017-02-07T11:23:50.667 回答