为什么使用元组而不是对象?

Mr.*_* 安宇 44 c++ tuples

我工作的代码库有一个名为Pair的对象,其中A和B是Pair中第一个和第二个值的类型.我发现这个对象是冒犯性的,因为它被用来代替具有明确命名成员的对象.所以我发现这个:

List<Pair<Integer, Integer>> productIds = blah();
// snip many lines and method calls

void doSomething(Pair<Integer, Integer> id) {
  Integer productId = id.first();
  Integer quantity = id.second();
}
Run Code Online (Sandbox Code Playgroud)

代替

class ProductsOrdered {
  int productId;
  int quantityOrdered;
  // accessor methods, etc
}

List<ProductsOrderded> productsOrdered = blah();
Run Code Online (Sandbox Code Playgroud)

代码库中Pair的许多其他用途同样难闻.

我用谷歌搜索了元组,它们似乎经常被误解或以可疑的方式使用.对他们的使用是否存在令人信服的论据?我可以理解不想创建庞大的类层次结构,但是如果没有使用元组,那么类层次结构会爆炸吗?

rlb*_*ond 36

首先,一个元组是快速而简单的:不是每次想要将两个东西放在一起时编写一个类,而是有一个模板可以为你完成.

其次,它们是通用的.例如,在C++中,std :: map使用std :: pair of key和value.因此,可以使用ANY对,而不必为两种类型的每个排列使用访问器方法创建某种包装类.

最后,它们对于返回多个值非常有用.实际上没有理由专门为一个函数的多个返回值创建一个类,如果它们不相关,它们不应该被视为一个对象.

公平地说,你粘贴的代码是一对使用不当.

  • 只是在这里扮演魔鬼的拥护者,但是在很多情况下你需要一个返回多个不相关值的方法吗?我经常看到的一个场景是C#中内置数据类型的`TryParse`方法.在这里,你需要返回一个`bool`(解析是否成功),比如一个`int`(如果解析成功则返回值).要允许第二个返回值,使用`out`关键字.我猜这里的元组可能不错,但我不确定它是否比声明一个名为`ParseSuccessful`的`bool`和一个名为`Value`的通用`T`的类型更好. (5认同)

Aar*_*paa 16

元组一直在Python中使用,它们被集成到语言中并且非常有用(它们允许启动器有多个返回值).

有时,你真的只需要配对并创造一个真实的,诚实的上帝,上课是矫枉过正的.另一方面,当你真的应该使用一个类时使用元组就像反过来一样.


Guf*_*ffa 10

代码示例有几个不同的气味:

  • 重新发明轮子

框架中已经有一个元组; 的KeyValuePair结构.这是所使用的Dictionary类来存储对,但是你可以用它在任何地方它适合.(并不是说它适合这种情况......)

  • 制作方形轮

如果你有一个对列表,那么使用该KeyValuePair结构比具有相同目的的类更好,因为它会导致更少的内存分配.

  • 隐藏意图

具有属性的类清楚地显示了值的含义,而Pair<int,int>类没有告诉您关于值表示的内容(只是它们可能以某种方式相关).为了使代码合理地用一个列表来解释,你必须给列表一个非常具有说服力的名称,比如productIdAndQuantityPairs...


Jul*_*iet 8

值得一提的是,OP中的代码是一团糟,不是因为它使用了元组,而是因为元组中的值太弱了.比较以下内容:

List<Pair<Integer, Integer>> products_weak = blah1();
List<Pair<Product, Integer>> products_strong = blah2();
Run Code Online (Sandbox Code Playgroud)

如果我的开发团队传递ID而不是类实例,我也会感到沮丧,因为整数可以代表任何东西.


话虽如此,当您正确使用它们时,元组非常有用:

  • 存在元组以将ad hoc值组合在一起.它们肯定比创建过多的包装类更好.
  • 当需要从函数返回多个值时,有用的替代out/ref参数.

然而,C#中的元组使我的眼睛充满水分.许多语言,如OCaml,Python,Haskell,F#等,都有一个特殊的,简洁的语法来定义元组.例如,在F#中,Map模块定义了一个构造函数,如下所示:

val of_list : ('key * 'a) list -> Map<'key,'a>
Run Code Online (Sandbox Code Playgroud)

我可以使用以下方法创建地图实例:

(* val values : (int * string) list *)
let values = 
    [1, "US";
     2, "Canada";
     3, "UK";
     4, "Australia";
     5, "Slovenia"]

(* val dict : Map<int, string> *)
let dict = Map.of_list values
Run Code Online (Sandbox Code Playgroud)

C#中的等价代码是荒谬的:

var values = new Tuple<int, string>[] {
     new Tuple<int, string>(1, "US"),
     new Tuple<int, string>(2, "Canada"),
     new Tuple<int, string>(3, "UK"),
     new Tuple<int, string>(4, "Australia"),
     new Tuple<int, string>(5, "Slovenia")
     }

 var dict = new Dictionary<int, string>(values);
Run Code Online (Sandbox Code Playgroud)

我不认为元组原则上有任何问题,但C#的语法太麻烦,无法充分利用它们.


jal*_*alf 5

它是代码重用。而不是编写另一个结构与我们制作的最后5个类似Tuple的类完全相同的类,您可以创建一个Tuple类,并在需要元组时使用它。

如果该类的唯一含义是“存储一对值”,那么我想说使用元组是一个显而易见的主意。我要说的是,如果您开始实现多个相同的类,以便可以重命名两个成员,那是一种代码味道(就象我讨厌这个词一样)。