重载与泛型参数

Posted

技术标签:

【中文标题】重载与泛型参数【英文标题】:Overloads vs generic arguments 【发布时间】:2010-11-22 08:04:09 【问题描述】:

我有一个问题。在框架中,这主要是在泛型出现之前编写的,你经常看到一个函数有很多重载来做不同类型的事情。

a)

Parse(int data)
Parse(long data)
Parse(string data)
..etc

这似乎没问题,因为它有助于使每种方法的代码保持较小等等。另一方面,现在使用泛型您可以执行以下操作:

b)

Parse<T>(T data)

然后有一些带有 typeof() 的 ifs/switch 语句来尝试推断类型是什么以及如何处理它们。

什么是最佳实践?或者有什么想法可以帮助我在 a) 和 b) 之间做出选择?

【问题讨论】:

最佳实践是:当您实现将泛型应用于任意多种类型的东西时,使用generics。当您有特定数量的备选方案时使用重载。 【参考方案1】:

恕我直言,如果你需要 if/switch 语句,你最好重载。在实现不依赖于具体类型仍可重用它的情况下,应使用泛型。

所以作为一般规则:

如果每种类型都有单独的实现,则重载 如果您可以有一个适用于所有可能类型的单一实现,请使用泛型。

【讨论】:

如果您有几种具有特定行为的类型和其余类型的默认行为,它可能会有点模糊。 当然,对于一堆简单的情况,这是一个简单的规则,但它并不总是那么简单。如果它变得复杂,可以考虑实现某种模式,如策略、访问者或其他。 @John - 您可以同时拥有 Parse(int data)(以及其他特定类型)和 Parse&lt;T&gt;(T data) 用于其他任何内容。如果传入特定的重载类型之一,将使用非泛型方法,否则将由泛型方法推断类型参数。 我同意这一点,因为一般来说,如果你传递一个它不支持的类型会发生什么?比在运行时抛出异常更容易出现编译器错误。【参考方案2】:

代码气味。

如果您有“某种 if/switch”,那是一种代码味道,只会尖叫多态性。它表明泛型不是解决该问题的方法。当代码不依赖于您传递给它的具体类型时,应使用泛型。

观看此 Google 技术讲座视频:“The Clean Code Talks -- Inheritance, Polymorphism, & Testing”。它具体解决了您正在谈论的内容。

【讨论】:

呸……没有什么比程序员试图用整个臃肿的类层次结构替换完全普通和简单的 if 语句的代码更难阅读的了。 @jalf Yuck... 没有什么比代码更难维护了维护代码:一个新的选项只是一个新的类和一个新的方法,而不是对项目中的所有代码进行完整的搜索! @DanibISHOP 哦,我看到你只是凭空发明了“一遍又一遍”的部分。可悲的是,它与我的评论或我评论的答案无关。答案是如果你有某种 if/switch,那就是代码异味。它没有提到重复。 @jalf 我只是试图以某种讽刺的方式为一般目的添加一些内容。这个答案使用了 M. Fowler 的“气味”概念,这个例子是一个 Parse() 函数,带有一个漂亮的“等等......”。从那里没有太多稀薄的空气:D【参考方案3】:

您所描述的使用泛型导致一堆 ifs/switch 语句的模式是一种反模式。

对此的一种解决方案是实现一种策略模式,它允许您使用泛型,但同时将 Parse 方法的关注点与知道如何处理每种不同的情况隔离开来。

例子:

class SomeParser

    void Parse<T>(ParseStrategy<T> parseStrategy, T data)
    
        //do some prep

        parseStrategy.Parse(data);

        //do something with the result;
    


class ParseStrategy<T>

    abstract void Parse(T data);


class StringParser: ParseStrategy<String>

    override void Parse(String data)
    
        //do your String parsing.
    


class IntParser: ParseStrategy<int>

    override void Parse(int data)
    
        //do your int parsing.
    


//use it like so
[Test]
public void ParseTest()

    var someParser = new SomeParser();
    someParser.Parse(new StringParser(), "WHAT WILL THIS PARSE TO");

然后您将能够传递您制定的任何策略。这将允许您在多个类中正确隔离您的关注点,并且不会违反 SRP(单一责任原则。

【讨论】:

【参考方案4】:

这里有一个问题 - 如果您需要 if/switch 语句来使泛型工作,那么您可能会遇到更大的问题。在这种情况下,泛型参数很可能对每种类型都不起作用(正确),只是您正在处理的一组固定类型。在这种情况下,最好提供重载来单独处理特定类型。

这有很多优点:

没有滥用的可能性 - 您的用户不能传递无效的参数 您的 API 更加清晰 - 很明显哪些类型是合适的 通用方法使用起来更复杂,对于初学者来说并不那么明显 泛型的使用表明任何类型都是有效的 - 它们确实应该适用于每种类型 在性能方面,泛型方法可能会更慢

如果你的论点可以适用于任何类型,这就变得不那么清楚了。在这种情况下,我仍然经常考虑包括重载,以及类型的通用后备方法。当您将“预期”类型传递给方法时,这会提高性能,但您仍然可以使用其他非预期类型。

【讨论】:

【参考方案5】:

虽然对此没有一刀切的规则,但当特定类型不相关时应使用泛型。这并不是说您不能限制类型,但特定类型实际上并不重要。在这种情况下,解析方法完全取决于(并且不同)每种类型。泛型在这里似乎不是一个好的解决方案。

【讨论】:

以上是关于重载与泛型参数的主要内容,如果未能解决你的问题,请参考以下文章

为啥将 Collections.emptySet() 与泛型一起使用在赋值中而不是作为方法参数?

Java数组:数组与泛型

Java ?不定类型与泛型的使用解决List继承参数

Java ?不定类型与泛型的使用解决List继承参数

模板与泛型编程——定义模板

模板与泛型编程