可以在.Net 4.0中使用元组的实际示例?

Ami*_*abh 97 .net c# vb.net .net-4.0 c#-4.0

我已经看到.Net 4中引入了元组,但我无法想象它可以在哪里使用.我们总是可以创建一个Custom类或Struct.

tan*_*ius 83

这就是重点 - 不要一直制作自定义类或结构更方便.这是一种改进,Action或者Func......你可以自己制作这种类型,但它们存在于框架中很方便.

  • 可能值得从[MSDN](https://msdn.microsoft.com/en-us/library/system.tuple(v = vs.110).aspx)中指出:*"此类型的任何公共静态成员都是线程安全.**不保证任何实例成员都是线程安全的.**"* (5认同)

Mar*_*oVW 74

使用元组,您可以轻松实现二维字典(或者就此而言是n维字典).例如,您可以使用此类字典来实现货币交换映射:

var forex = new Dictionary<Tuple<string, string>, decimal>();
forex.Add(Tuple.Create("USD", "EUR"), 0.74850m); // 1 USD = 0.74850 EUR
forex.Add(Tuple.Create("USD", "GBP"), 0.64128m);
forex.Add(Tuple.Create("EUR", "USD"), 1.33635m);
forex.Add(Tuple.Create("EUR", "GBP"), 0.85677m);
forex.Add(Tuple.Create("GBP", "USD"), 1.55938m);
forex.Add(Tuple.Create("GBP", "EUR"), 1.16717m);
forex.Add(Tuple.Create("USD", "USD"), 1.00000m);
forex.Add(Tuple.Create("EUR", "EUR"), 1.00000m);
forex.Add(Tuple.Create("GBP", "GBP"), 1.00000m);

decimal result;
result = 35.0m * forex[Tuple.Create("USD", "EUR")]; // USD 35.00 = EUR 26.20
result = 35.0m * forex[Tuple.Create("EUR", "GBP")]; // EUR 35.00 = GBP 29.99
result = 35.0m * forex[Tuple.Create("GBP", "USD")]; // GBP 35.00 = USD 54.58
Run Code Online (Sandbox Code Playgroud)


Han*_*ant 26

在MSDN杂志上有一篇很好的文章,讨论了将Buple添加到BCL的肚子痛和设计考虑因素.在值类型和引用类型之间进行选择尤其有趣.

正如文章明确指出的那样,Tuple背后的驱动力是微软内部的许多团队都在使用F#团队.虽然没有提到,但我认为C#(和VB.NET)中新的"动态"关键字也与它有关,元组在动态语言中很常见.

它不是特别优于创建自己的poco,至少你可以给会员一个更好的名字.


更新:由于C#版本7的重大修订,现在获得了更多的语法爱.在这篇博客文章中的初步公告.


Tej*_*ejs 23

这是一个小例子 - 假设您有一个方法需要查找用户的句柄和电子邮件地址,给定用户ID.您始终可以创建包含该数据的自定义类,或者对该数据使用ref/out参数,或者您只需返回一个元组并拥有一个很好的方法签名,而无需创建新的POCO.

public static void Main(string[] args)
{
    int userId = 0;
    Tuple<string, string> userData = GetUserData(userId);
}

public static Tuple<string, string> GetUserData(int userId)
{
    return new Tuple<string, string>("Hello", "World");
}
Run Code Online (Sandbox Code Playgroud)

  • 另一个很好的例子是int.TryParse,因为你可以消除输出参数而不是使用元组.所以你可以使用`Tuple <bool,T> TryParse <T>(字符串输入)`而不必使用输出参数,你可以在元组中得到两个值. (21认同)
  • 这是一个很好的例子,但不能证明使用Tuple. (10认同)
  • 一个元组在这里是一个不错的选择,因为你返回了不同的值; 但是当你返回不同*类型*的多个值时,元组会更加闪耀. (7认同)
  • 实际上,这正是从F#调用任何TryParse方法时会发生的情况. (3认同)

Cra*_*ntz 23

我用一个元组来解决Euler项目的问题11:

class Grid
{
    public static int[,] Cells = { { 08, 02, 22, // whole grid omitted

    public static IEnumerable<Tuple<int, int, int, int>> ToList()
    {
        // code converts grid to enumeration every possible set of 4 per rules
        // code omitted
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我可以用以下方法解决整个问题:

class Program
{
    static void Main(string[] args)
    {
        int product = Grid.ToList().Max(t => t.Item1 * t.Item2 * t.Item3 * t.Item4);
        Console.WriteLine("Maximum product is {0}", product);
    }
}
Run Code Online (Sandbox Code Playgroud)

可以使用自定义类型,但它看起来就像Tuple.


Jul*_*iet 16

C#的元组语法非常笨重,所以元组很难说出来.并且它没有模式匹配,所以它们也很难使用.

但偶尔,您只需要一个特殊的对象分组,而无需为其创建类.例如,假设我想聚合一个列表,但我想要两个值而不是一个:

// sum and sum of squares at the same time
var x =
    Enumerable.Range(1, 100)
    .Aggregate((acc, x) => Tuple.Create(acc.Item1 + x, acc.Item2 + x * x));
Run Code Online (Sandbox Code Playgroud)

我们不是将一组值组合成一个结果,而是将一个结果扩展为一组值.编写此函数的最简单方法是:

static IEnumerable<T> Unfold<T, State>(State seed, Func<State, Tuple<T, State>> f)
{
    Tuple<T, State> res;
    while ((res = f(seed)) != null)
    {
        yield return res.Item1;
        seed = res.Item2;
    }
}
Run Code Online (Sandbox Code Playgroud)

f将某个状态转换为元组.我们从元组返回第一个值并将我们的新状态设置为第二个值.这允许我们在整个计算过程中保持状态.

你这样使用它:

// return 0, 2, 3, 6, 8
var evens =
    Unfold(0, state => state < 10 ? Tuple.Create(state, state + 2) : null)
    .ToList();

// returns 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
var fibs =
    Unfold(Tuple.Create(0, 1), state => Tuple.Create(state.Item1, Tuple.Create(state.Item2, state.Item1 + state.Item2)))
    .Take(10).ToList();
Run Code Online (Sandbox Code Playgroud)

evens相当简单,但fibs更聪明一点.它state实际上是一个元组,分别包含fib(n-2)和fib(n-1).

  • +1 Tuple.Create是`new Tuple <Guid,string,...>`的便捷简写 (4认同)

Not*_*sxl 7

我不喜欢它们的滥用,因为它们产生的代码本身并不能解释,但它们很棒实现动态复合键,因为它们实现了IStructuralEquatable和IStructuralComparable(用于查找和排序)目的).

他们在内部组合了所有项目的哈希码; 例如,这里是Tuple的GetHashCode(取自ILSpy):

    int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
    {
        return Tuple.CombineHashCodes(comparer.GetHashCode(this.m_Item1), comparer.GetHashCode(this.m_Item2), comparer.GetHashCode(this.m_Item3));
    }
Run Code Online (Sandbox Code Playgroud)


And*_*law 6

元组非常适合一次执行多个异步IO操作并将所有值一起返回.以下是使用和不使用Tuple的示例.元组实际上可以使您的代码更清晰!

没有(讨厌的嵌套!):

Task.Factory.StartNew(() => data.RetrieveServerNames())
    .ContinueWith(antecedent1 =>
        {
            if (!antecedent1.IsFaulted)
            {
                ServerNames = KeepExistingFilter(ServerNames, antecedent1.Result);
                Task.Factory.StartNew(() => data.RetrieveLogNames())
                    .ContinueWith(antecedent2 =>
                        {
                            if (antecedent2.IsFaulted)
                            {
                                LogNames = KeepExistingFilter(LogNames, antecedent2.Result);
                                Task.Factory.StartNew(() => data.RetrieveEntryTypes())
                                    .ContinueWith(antecedent3 =>
                                        {
                                            if (!antecedent3.IsFaulted)
                                            {
                                                EntryTypes = KeepExistingFilter(EntryTypes, antecedent3.Result);
                                            }
                                        });
                            }
                        });
            }
        });
Run Code Online (Sandbox Code Playgroud)

随着元组

Task.Factory.StartNew(() =>
    {
        List<string> serverNames = data.RetrieveServerNames();
        List<string> logNames = data.RetrieveLogNames();
        List<string> entryTypes = data.RetrieveEntryTypes();
        return Tuple.Create(serverNames, logNames, entryTypes);
    }).ContinueWith(antecedent =>
        {
            if (!antecedent.IsFaulted)
            {
                ServerNames = KeepExistingFilter(ServerNames, antecedent.Result.Item1);
                LogNames = KeepExistingFilter(LogNames, antecedent.Result.Item2);
                EntryTypes = KeepExistingFilter(EntryTypes, antecedent.Result.Item3);
            }
        });
Run Code Online (Sandbox Code Playgroud)

如果您正在使用有一个隐含的类型匿名函数反正那么你是不是让代码使用元组不太清楚.从方法中重新调整元组?在我的拙见中,当代码清晰度是关键时谨慎使用.我知道C#中的函数式编程难以抗拒,但我们必须考虑所有那些旧的笨重的"面向对象"的C#程序员.


Man*_*101 5

元组在函数式语言中被大量使用,它们可以用它们做更多的事情,现在F#是一种"官方".net语言,你可能希望从C#中与它进行互操作,并在用两种语言编写的代码之间传递它们.


Joh*_*lak 5

我倾向于避免Tuple大多数情况,因为它会损害可读性.但是,Tuple在需要对不相关的数据进行分组时非常有用.

例如,假设您有一个汽车列表以及购买汽车的城市:

Mercedes, Seattle
Mustang, Denver
Mercedes, Seattle
Porsche, Seattle
Tesla, Seattle
Mercedes, Seattle
Run Code Online (Sandbox Code Playgroud)

您想要汇总每个城市每辆车的计数:

Mercedes, Seattle [3]
Mustang, Denver [1]
Porsche, Seattle [1]
Tesla, Seattle [1]
Run Code Online (Sandbox Code Playgroud)

为此,您创建一个Dictionary.你有几个选择:

  1. 创建一个Dictionary<string, Dictionary<string, int>>.
  2. 创建一个Dictionary<CarAndCity, int>.
  3. 创建一个Dictionary<Tuple<string, string>, int>.

第一个选项丢失了可读性.它将要求您编写更多代码.

第二个选项有效且简洁,但是汽车和城市并没有真正相关,可能并不属于同一类.

第三种选择是简洁和干净.这是一个很好的用途Tuple.