【问题标题】:How do I dispatch to a method based on a parameter's runtime type in C# < 4?如何在 C# < 4 中根据参数的运行时类型调度方法?
【发布时间】:2010-05-12 11:46:43
【问题描述】:

我有一个对象o,它保证在运行时是ABC 三种类型之一,它们都实现了一个公共接口I。我可以控制I,但不能控制ABC。 (因此我可以使用空标记接口,或者通过使用接口以某种方式利用类型中的相似性,但我不能添加新方法或更改类型中的现有方法。)

我还有一系列方法MethodAMethodBMethodC。查找o 的运行时类型,然后将其用作这些方法的参数。

public void MethodA(A a) { ... }
public void MethodB(B b) { ... }
public void MethodC(C c) { ... }

使用这种策略,现在必须检查o 的类型以确定应该调用哪个方法。相反,我想简单地拥有三个重载方法:

public void Method(A a) { ... } // these are all overloads of each other
public void Method(B b) { ... }
public void Method(C c) { ... }

现在我让 C# 进行调度,而不是自己手动进行。这可以做到吗?天真的直接方法当然行不通:

无法解析方法“方法(对象)”。候选人是:

  • 无效方法(A)
  • 无效方法(B)
  • 无效方法(C)

【问题讨论】:

  • 如果有 D、E 和 F 类型,它们是否也都需要该方法的实现?
  • @Jason:大概是的,但在我的特殊情况下,其他此类类型不太可能存在。
  • 顺便说一句:+1 对一个很好且问得很好的问题。很明显,您知道自己的选择并了解良好的设计实践。

标签: c# methods dispatch


【解决方案1】:

如果可以重构,请将方法移至接口并让每个类都有其实现:

I i = o as I;
i.Method();

【讨论】:

  • 我编辑澄清我控制I,但不是ABC。但如果可以的话,那就太好了!
  • @Evan,使用部分类(见我的回答)
  • @Evan - 你如何得到o?如果您控制返回它的函数,您可以从 A B 和 C 继承,并让新类实现新接口。我想你也不能控制它,所以你不会得到新的类型......
  • @tster 如果你将一个类设为部分,那么它的所有部分声明必须在同一个程序集中进行,并且在类的所有声明中必须标记为部分,所以你仍然需要控制A,@ 987654328@ 和 C 如果你可以控制它,你也可以按照 Kobi 的建议重构代码。
  • @Cornelius,事实上,A、B 和 C 都继承自他确实控制的 I,这让我认为他对类本身至少有一点控制权。我想,这就是这个问题如此奇怪的原因。
【解决方案2】:

这样的事情怎么样?

private Dictionary<Type, Action<I>> _mapping = new Dictionary<Type, Action<I>>
{ 
  { typeof(A), i => MethodA(i as A)},
  { typeof(B), i => MethodB(i as B)},
  { typeof(C), i => MethodC(i as C)},
};

private void ExecuteBasedOnType(object value)
{
    if(_mapping.ContainsKey(value.GetType()))
       _mapping(value.GetType())(value as I);
}

【讨论】:

  • 哈,这实际上是我目前拥有的!不过,显然它不如让 C# 以某种方式进行调度。
  • 如果您可以控制 I、A、B 和 C,您可以在 I 中添加一些名为 ExecuteMethod() 的内容,然后在 I 上调用它。如果没有,您必须以某种方式对其进行映射。
  • 如果您不能应用 Kobi 的建议,这是您的最佳选择。除非您也控制类,否则无法完成双重调度/访问者模式。
  • 我认为这是最好的选择,所以我接受这个答案。
  • 如果您接受此建议并将_mapping 设为静态并将ExecuteBasedOnType 更改为I 的扩展方法,那么您将获得Kobi 建议中的良好语法,而无需重构A、@987654326 @和C
【解决方案3】:

如果 3 类 A、B 和 C 实现了接口 I 你应该什么都不做。为您选择正确方法的是运行时:

A a = new A();
class.Method(a); // will calll Method(A a)

B b = new B();
class.Method(b); // will call Method(B b)

【讨论】:

  • 这不能正确回答问题(或者至少误解了我的意思)。我没有AB;我有一个object(见第一句话)。
【解决方案4】:

我不能 100% 确定这是否适用于您的场景,但听起来您可以使用部分类:

public interface IMethodCallable
{
    public void Method();
}

public partial class A : IMethodCallable
{
    public void Method()
    {
        ....
    }
}

那么对于用法:

object o = getObject(); // we know this is A, B or C
((IMethodCallable)o).Method();

【讨论】:

    【解决方案5】:

    大概 O 被声明为类型/接口 I 然后实例化为 a b 或 c。做到这一点的方法是使用一个公共方法来获取类型 I 的参数,然后在该方法中执行您的逻辑,例如调用私有方法 A B 或 C。

    阅读您编辑过的帖子,o 属于 object 类型,请记住在使用 object 时要清楚,因为作为 c# 类型,它是类实例的通用 oop 术语。

    你为什么把它声明为一个对象,而不是一个i,以后会不会是别的东西?

    如果是这样,要使用您的重载方法,您必须在调用该方法之前将其拆箱。例如

    if (o.GetType() == typeof(a))
    {
       oa = (a)o;
       Method(oa);
    }
    else if(o.GetType() == typeof(b))
    {
        ...
    }
    

    等等

    或者,如果您使该方法采用 i 类型的参数,您可以执行以下操作:

    i oi = (i)o;
    Method(oi);
    

    但您仍然需要在公共方法中执行第一个示例。

    【讨论】:

      猜你喜欢
      • 2019-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-13
      相关资源
      最近更新 更多