【问题标题】:conditional logic based on type基于类型的条件逻辑
【发布时间】:2008-10-06 03:49:29
【问题描述】:

给定:

interface I
{
}

class B: I
{
}

class C: I
{
}

class A
{

    public void Method(B arg)
    {
    }

    public void Method(C arg)
    {
    }

    public void Method(I arg)
    {
       // THIS is the method I want to simplify.
       if (I is B)
       {
          this.Method(arg as B);
       }
       else if (I is C)
       {
          this.Method(arg as C);
       }
    }
}

我知道有更好的方法来设计这种类型的交互,但是因为 无法解释需要很长时间才能解释的细节。 由于这种模式会被重复很多次,我想替换 具有通用实现的条件逻辑,我只能使用一行。 我看不到实现这个通用方法/类的简单方法,但我的直觉告诉我应该是可能的。

任何帮助将不胜感激。

【问题讨论】:

  • 哪个模式重复了很多次?类型检查?

标签: c# .net polymorphism


【解决方案1】:

我会把方法放在接口里面,然后让多态决定调用哪个方法

interface I
{
   void Method();
}

class B : I
{
   public void Method() { /* previously A.Method(B) */}
}

class C : I
{
   public void Method() { /* previously A.Method(C) */ }
}

class A
{
   public void Method(I obj)
   { 
     obj.Method();
   }
}

现在当你需要添加一个新类时,你只需要实现I.Method。您无需触摸 A.Method。

【讨论】:

  • 这在某些情况下有效,但有时它不合适甚至不可能。
  • 在这些情况下,为什么不使用 new 和 virtual 修饰符?
  • 当然,这些场景必须在此处列出,以便我们更改代码来处理。
  • 是的! “基于类型的条件逻辑”是敲响“使用多态性!”的红色警钟
  • @Grauenwolf,除非您提供更具体的示例,否则人们无法帮助您提供适合您的解决方案。仅仅说“有时不合适”并不能提供足够的信息让人们帮助你。
【解决方案2】:

你想要的是double dispatch,尤其是visitor pattern

【讨论】:

    【解决方案3】:

    这有点难看,但它可以完成工作:

    public void Method(B arg)
    {
      if (arg == null) return;
    ...
    }
    public void Method(C arg)
    {
      if (arg == null) return;
    ...
    }
    
    public void Method(I arg)
    {
      this.Method(arg as B);
      this.Method(arg as C);
    }
    

    不过,我不认为我会这样做。看着真的很心疼。对不起,我强迫你们也看看这个。

    【讨论】:

    • 不是我会做的方式,但无论如何都是一种有趣的技术。
    【解决方案4】:
    interface I
    { 
    } 
    
    class B : I
    {
    }
    
    class C : I
    {
    }    
    
    class A 
    {
        public void Method(B arg)
        {
            Console.WriteLine("I'm in B");
        }
    
        public void Method(C arg)
        {
            Console.WriteLine("I'm in C");
        }
    
        public void Method(I arg)
        {
            Type type = arg.GetType();
    
            MethodInfo method = typeof(A).GetMethod("Method", new Type[] { type });
            method.Invoke(this, new I[] { arg });
        }
    }
    

    【讨论】:

    【解决方案5】:

    对于基于 F# 模式匹配的想法,它不以 C# 的方便形式存在 - see here,这正是你想要的。您可以通过反射做一些事情来在运行时选择重载,但这会非常慢,并且如果有任何东西同时满足这两个重载,则会出现严重问题。如果您有返回值,则可以使用条件运算符;

    return (I is B) ? Method((B)I) : ((I is C) ? Method((C)I) : 0);
    

    再次 - 不漂亮。

    【讨论】:

    • 当然可以。您所要做的就是使用 CallByName 函数。或者,如果您愿意,可以直接使用 Reflection。
    • 那次投票(如果你)是 IMO 没有根据的; CallByName 不是 C# 语言功能(OP 是 C#,而不是 VB) - 它是基于反射的运行时功能,由于没有正式的偏好序列,因此速度明显缓慢且脆弱。
    • Re the "brittle" - 在一般情况下,当类型匹配两个重载时,你会遇到真正的问题 - 即在 C# 中你会得到错误 CS0121: The call is ambiguous between the following methods or属性
    • CallByName 只是一个存在于随 .NET 框架分发的程序集中的函数。它的名称中有“VisualBasic”这一事实并不意味着 C# 不能使用它。至于易碎性,其他所有动态类型技术也是如此。
    【解决方案6】:

    简单。在 Visual Basic 中,我一直使用 CallByName 来执行此操作。

    Sub MethodBase(value as Object)
        CallByName(Me, "RealMethod", CallType.Method, value)
    

    这将调用与值的运行时类型最匹配的 RealMethod 的重载。

    我相信您可以通过导入 Microsoft.VisualBasic.Interaction 或使用反射创建您自己的版本来使用 C# 中的 CallByName。

    【讨论】:

    • 查看我对您的评论的回复,为什么这不是一个好主意(除了它很慢而且 CAS 可能不允许这样做......)
    猜你喜欢
    • 1970-01-01
    • 2016-11-19
    • 1970-01-01
    • 2014-05-03
    • 1970-01-01
    • 2016-08-16
    • 1970-01-01
    • 1970-01-01
    • 2019-04-12
    相关资源
    最近更新 更多