【问题标题】:How to make a method take 2 different types如何使方法采用两种不同的类型
【发布时间】:2010-09-09 20:51:12
【问题描述】:

我知道这可能是一个非常简单的问题,但我现在脑子里放了个屁。我正在尝试创建一种可以采用两种自定义类型之一的方法。基本上这种方法的主体对于这两种类型都是相同的,因为它们都有一个 Name 属性(我正在对 Name 属性进行比较以用于排序)。我该怎么做?

我的第一个想法就是用这两种类型作为参数重载方法:

int Compare(Type1 first, Type1 second)
int Compare (Type2 first, Type2 second)

但是方法的主体最终是相同的,因此这似乎是一种浪费。

我的下一个想法是使用泛型,但这似乎不对,因为我并没有真正将其设为泛型,因为它只能用于 2 种特定类型。

澄清:“自定义”类型实际上不是我的自定义类型。我的意思是它们不是内置类型。我无法控制这些类型或继承层次结构中的内容。他们只是碰巧都有 Name 属性。

【问题讨论】:

    标签: c# generics overloading


    【解决方案1】:

    奇怪的是,到目前为止,还没有人发布在我看来是显而易见的选择:将名称作为比较的参数。让 调用者 从对象中获取 name 属性。如果所有方法需要使用的只是名称,那你为什么要传入对象的其余部分?

    如果你需要抽象出“我可以从这件事中得到一个名字”的概念,那么就这样做吧:

    例子:

    int Compare(string name1, string name2) { ... whatever ... }
    int Compare<T>(T t1, T t2, Func<T, string> getName)
    {
        return Compare(getName(t1), getName(t2));
    }
    

    现在你可以说:

    Compare(car1, car2, c=>c.Name);
    Compare(employee1, employee2, e=>e.Name);
    

    等等。

    【讨论】:

    • @Porges 基本上确实发布了这个回复(或相当相似)。实际上,除了 Name 之外,我还进行了其他比较,所以这不太奏效。为简单起见 +1。
    【解决方案2】:

    如果这两种类型派生自相同的基类或使用相同的接口,并且没有其他类使用基类和/或接口,则泛型仍然是一个选项。

    int Compare<T>(T first, T second) where T : IHasName
    

    简而言之,拥有两个重载是可行的方法。

    注意:如果你有 .NET 4,你可以让它成为一个运行时的东西,并使方法动态化。

    int Compare(dynamic first, dynamic second)
    

    在这种情况下,你扔给它的任何东西都会编译,但如果Name 属性不存在,它会在运行时崩溃。

    就个人而言,如果我不能修改类型以使用通用接口,那么我会使用重载并获得编译时安全性。

    【讨论】:

    • 让两种类型都实现一个通用接口似乎是恕我直言的方法。
    • 两者都不可能。它们不是从同一个基类派生的,而且这两种类型不是我编写的,所以我无法更改。
    • 即使类型不是你写的,你还能走重载路线吗?
    • @Kris,添加了关于 .NET 4 和动态的注释。但在你的情况下,我可能会坚持使用重载。
    • 重载仍然是一种选择,但您将无法合并这两种方法的代码。如果这两种类型的对象足够相似,您可以使用几乎相同的代码为每种类型编写一个方法,那么我同意 Chris 的观点;他们应该实现一个通用接口。你能让作者更新这些类的声明吗?如果这些组件真的是不连贯的组件,您将无法让它们共享一个公共接口,我认为您能做的最好的事情就是围绕每种类型编写某种包装类。
    【解决方案3】:

    好的,我在第一个答案中完全错误地阅读了您的问题。这是一个更好的:)

    重载是您最好的选择。由于比较结果只依赖于Name,所以做一个that比较的方法,然后调用它:

    private int Compare(string first, string second)
    {
        // do comparison
    }
    
    public int Compare(Type1 first, Type1 second)
    {
        return Compare(first.Name, second.Name):
    }
    
    public int Compare(Type2 first, Type2 second)
    {
        return Compare(first.Name, second.Name);
    }
    

    编辑:

    由于您拥有多个项目,因此您可以做两件事:

    public int Compare(string first1, string second1, X first2, X second2 ...
    

    但这会变得有点难看。另一种方法是提供投影以提取值:

    private int Compare<T>(T first, T second, Func<T,Tuple<string,int,TypeX,TypeY>> projection)
    {
        // test for first==null, second==null, etc...
    
        var nicerFirst = projection(first);
        var nicerSecond = projection(second);
    
        // compare objects using nicerFirst.Item1, nicerFirst.Item2, etc.
    }
    

    然后你的比较看起来像:

    int Compare(Type1 first, Type1 second)
    {
        return Compare(first, second, x => Tuple.Create(x.Name, x.Int, x.X, x.Y));
    }
    

    【讨论】:

    • 除了名称之外,我实际上还进行了其他比较,所以这不太有效。帮助了尽管如此 +1,谢谢!
    【解决方案4】:

    如果它只能与 2 种类型一起使用,则进行 2 次重载。

    【讨论】:

    • 我确实最终选择了这条路线,所以为你 +1,但我用更多选项将答案标记为正确,因为其他选项将来可能对其他人有更多帮助。
    【解决方案5】:

    重载该方法将是使其仅采用两种特定类型的好方法。为避免重复代码,您可以在后台使用泛型:

    public int Compare(Type1 first, Type1 second) {
      return Compare<Type1>(first, second);
    }
    
    public int Compare(Type2 first, Type2 second) {
      return Compare<Type2>(first, second);
    }
    
    private int Compare<T>(T first, T second) {
      ...
    }
    

    【讨论】:

    • 您仍然需要将T 限制为基接口/类,以便能够在其上调用特定方法...
    【解决方案6】:

    也许不是最好的解决方案,但这是我想到的: 你做了一个方法:

    int MainCompare(Type1 first1, Type1 second1, Type2 first2, Type2 second2, bool firsttype){...}
    

    然后您可以在一行中使用两种比较方法。

    int compare(Type1 first, Type1 second){
        return MainCompare(first, second, null, null, true);
    }
    
    
    int compare(Type2 first, Type2 second){
        return MainCompare(null, null, first, second, false);
    }
    

    您的 MainCompare 会根据它使用的类型而略有变化。

    【讨论】:

      【解决方案7】:

      这可能是一个过分的解决方案,但您可以为这两个类制作适配器,并让它们实现一个通用接口:

      public class MyType1 : ICommonInterface, Type1
      {
          // Where you can define 'Somethin' in the common interface
          public string Something 
          { 
              get { return base.Something; } 
              set { base.Something = value; } 
          }
      
           /* ... */
      }
      
      public class MyType2 : ICommonInterface, Type2
      {
          // If there is no base.Something on Type2, you can re-name it assuming 
          // its intent is the same as Something
          public string Something
          {
              get { return base.SomethingElse; } 
              set { set base.SomethingElse = value; }
          }        
      }
      

      然后您可以基本上完​​全实现@Anthony Pegram (+1) 的功能:

      int Compare<T> (T first, T second) where T : ICommonInterface
      {
          return first.Something.CompareTo(second.Something);
      }
      

      【讨论】:

        猜你喜欢
        • 2020-05-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-24
        • 1970-01-01
        相关资源
        最近更新 更多