C#中的类似于C++ std::pair的对象是什么?

338

我感兴趣:C#中与C++中的std::pair相对应的是什么?我发现了System.Web.UI.Pair类,但我更喜欢基于模板的解决方案。

谢谢!


11
我之前有个相同的请求,但越想越觉得你可能只是想自己编写一个配对类,使用明确的类类型和字段,而不是使用通用的“First”和“Second”。这会使你的代码更易读。一个配对类最少只需要4行代码,因此通过重复使用通用的Pair<T,U>类,你并没有省下多少内容,反而会导致代码难以理解。 - Mark Lakata
14个回答

382

元组(Tuples)自.NET4.0起可用,并支持泛型:

Tuple<string, int> t = new Tuple<string, int>("Hello", 4);

在之前的版本中,你可以使用 System.Collections.Generic.KeyValuePair<K, V> 或者像以下这样的解决方案:
public class Pair<T, U> {
    public Pair() {
    }

    public Pair(T first, U second) {
        this.First = first;
        this.Second = second;
    }

    public T First { get; set; }
    public U Second { get; set; }
};

并像这样使用它:

Pair<String, int> pair = new Pair<String, int>("test", 2);
Console.WriteLine(pair.First);
Console.WriteLine(pair.Second);

这将输出:

test
2

甚至这种链式的对儿:
Pair<Pair<String, int>, bool> pair = new Pair<Pair<String, int>, bool>();
pair.First = new Pair<String, int>();
pair.First.First = "test";
pair.First.Second = 12;
pair.Second = true;

Console.WriteLine(pair.First.First);
Console.WriteLine(pair.First.Second);
Console.WriteLine(pair.Second);

这将输出:

test
12
true

1
请查看我的关于添加Equals方法的帖子。 - Andrew Stein
Tuple<>现在是一个更好的解决方案。 - dkantowitz
6
由于泛型类的类型参数无法在对象创建表达式(构造函数调用)中推断,BCL 的作者们创建了一个非泛型的帮助类称为 Tuple。因此,您可以使用 Tuple.Create("Hello", 4),比使用 new Tuple<string, int>("Hello", 4) 更加简单。(顺便说一句,.NET4.0自2010年以来已经发布。) - Jeppe Stig Nielsen
5
请注意,Tuple<> 实现了可靠的值语义的 EqualsGetHashCode,这很好。在实现自己的元组时要记住这一点。 - nawfal
这显然是因为Equals和GetHashCode出了问题。 - julx
不是[结构体],而是[类]?我认为在将[Pair]用作C#字典键时,它的工作方式与[std::pair]不同。 - siamenock

98

System.Web.UI 包含 Pair 类,因为它在 ASP.NET 1.1 中作为内部 ViewState 结构大量使用。

更新于2017年8月: C# 7.0 / .NET Framework 4.7 提供了一种语法,可以使用 System.ValueTuple 结构声明具有命名项的元组。

//explicit Item typing
(string Message, int SomeNumber) t = ("Hello", 4);
//or using implicit typing 
var t = (Message:"Hello", SomeNumber:4);

Console.WriteLine("{0} {1}", t.Message, t.SomeNumber);

查看 MSDN 获取更多语法示例。

更新于2012年6月:Tuples自.NET 4.0版本以来就成为了其中的一部分。

这里是一个早期文章,描述了在.NET 4.0中的包含和泛型支持:

Tuple<string, int> t = new Tuple<string, int>("Hello", 4);

2
请注意,元组是只读的。也就是说,您不能这样做:tuple.Item1 = 4; - skybluecodeflier
2
元组正是我正在寻找的。谢谢。 - gligoran

39

很遗憾,没有这个。在许多情况下,您可以使用 System.Collections.Generic.KeyValuePair<K, V>

或者,您可以使用匿名类型来处理元组,至少在本地可以这样做:

var x = new { First = "x", Second = 42 };

最后一个选择是创建自己的类。


2
只是为了明确起见,匿名类型也是只读的 - msdn - bsegraves

21

从4.0版本开始,C#引入了元组


10

有些答案看起来是错误的,

  1. 你不能使用字典存储(a,b)和(a,c)这样的对。对的概念不应与键值对的关联查找混淆。
  2. 上面很多代码似乎可疑。

这是我的Pair类

public class Pair<X, Y>
{
    private X _x;
    private Y _y;

    public Pair(X first, Y second)
    {
        _x = first;
        _y = second;
    }

    public X first { get { return _x; } }

    public Y second { get { return _y; } }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        if (obj == this)
            return true;
        Pair<X, Y> other = obj as Pair<X, Y>;
        if (other == null)
            return false;

        return
            (((first == null) && (other.first == null))
                || ((first != null) && first.Equals(other.first)))
              &&
            (((second == null) && (other.second == null))
                || ((second != null) && second.Equals(other.second)));
    }

    public override int GetHashCode()
    {
        int hashcode = 0;
        if (first != null)
            hashcode += first.GetHashCode();
        if (second != null)
            hashcode += second.GetHashCode();

        return hashcode;
    }
}

以下是一些测试代码:

[TestClass]
public class PairTest
{
    [TestMethod]
    public void pairTest()
    {
        string s = "abc";
        Pair<int, string> foo = new Pair<int, string>(10, s);
        Pair<int, string> bar = new Pair<int, string>(10, s);
        Pair<int, string> qux = new Pair<int, string>(20, s);
        Pair<int, int> aaa = new Pair<int, int>(10, 20);

        Assert.IsTrue(10 == foo.first);
        Assert.AreEqual(s, foo.second);
        Assert.AreEqual(foo, bar);
        Assert.IsTrue(foo.GetHashCode() == bar.GetHashCode());
        Assert.IsFalse(foo.Equals(qux));
        Assert.IsFalse(foo.Equals(null));
        Assert.IsFalse(foo.Equals(aaa));

        Pair<string, string> s1 = new Pair<string, string>("a", "b");
        Pair<string, string> s2 = new Pair<string, string>(null, "b");
        Pair<string, string> s3 = new Pair<string, string>("a", null);
        Pair<string, string> s4 = new Pair<string, string>(null, null);
        Assert.IsFalse(s1.Equals(s2));
        Assert.IsFalse(s1.Equals(s3));
        Assert.IsFalse(s1.Equals(s4));
        Assert.IsFalse(s2.Equals(s1));
        Assert.IsFalse(s3.Equals(s1));
        Assert.IsFalse(s2.Equals(s3));
        Assert.IsFalse(s4.Equals(s1));
        Assert.IsFalse(s1.Equals(s4));
    }
}

4
如果您不实现IEquatable接口,就会出现装箱问题。还需要更多的工作来正确完成您的类。 - Jack

9

除了自定义类或.Net 4.0元组之外,自C# 7.0以来,有一种名为ValueTuple的新功能可用于此情况。它是一个可以用于此情况的结构体。不需要编写以下代码:

Tuple<string, int> t = new Tuple<string, int>("Hello", 4);

通过 t.Item1t.Item2 访问值,你可以这样简单地实现:
(string message, int count) = ("Hello", 4);

甚至可以:
(var message, var count) = ("Hello", 4);

7
如果是关于字典等数据结构的话,您需要使用System.Collections.Generic.KeyValuePair<TKey, TValue>。

3

我创建了一个C#的元组实现,它可以通用地解决两个到五个值之间的问题 - 这里是博客文章,其中包含源代码的链接。


3

我通常将Tuple类扩展为我的通用包装器,具体做法如下:

public class Statistic<T> : Tuple<string, T>
{
    public Statistic(string name, T value) : base(name, value) { }
    public string Name { get { return this.Item1; } }
    public T Value { get { return this.Item2; } }
}

并且可以这样使用:

public class StatSummary{
      public Statistic<double> NetProfit { get; set; }
      public Statistic<int> NumberOfTrades { get; set; }

      public StatSummary(double totalNetProfit, int numberOfTrades)
      {
          this.TotalNetProfit = new Statistic<double>("Total Net Profit", totalNetProfit);
          this.NumberOfTrades = new Statistic<int>("Number of Trades", numberOfTrades);
      }
}

StatSummary summary = new StatSummary(750.50, 30);
Console.WriteLine("Name: " + summary.NetProfit.Name + "    Value: " + summary.NetProfit.Value);
Console.WriteLine("Name: " + summary.NumberOfTrades.Value + "    Value: " + summary.NumberOfTrades.Value);

2
自 .NET 4.0 以来,您可以使用 System.Tuple<T1, T2> 类:
// pair is implicitly typed local variable (method scope)
var pair = System.Tuple.Create("Current century", 21);

@Alexander,你可以轻松查看.NET 3.5元组文档 - Serge Mikhailov
在底部他们说: 版本信息 NET Framework 支持版本:4 - Alexander Prokofyev
2
@Alexander:好的,没错。(不过我还在想,为什么他们要把这个页面限定在.NET 3.5上) - Serge Mikhailov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接