zoukankan      html  css  js  c++  java
  • Tuple元组

    Tuple为何物?   msdn之Tuple详解

    如果不想给一个不常使用对象建一个类(Modle),可以使用Tuple来代替。

    在汉语上我们将其翻译为元组。Tuple的概念源于数学概念,表示有序的数据集合。在.NET中Tuple被实现为泛型类型,n-Tuple表示有n个元素的Tuple,集合的元素可以是任何类型。

    Example 1:

    // 创建一个 7-tuple.
    var population = new Tuple<string, int, int, int, int, int, int>(
                               "New York", 7891957, 7781984, 
                               7894862, 7071639, 7322564, 8008278);
    // 其他创建方式
    var population = Tuple.Create("New York", 7891957, 7781984, 7894862, 7071639, 7322564, 8008278);
    Console.WriteLine(
    "Population of {0} in 2000: {1:N0}", population.Item1, population.Item7);//output: Population of New York in 2000: 8,008,278

    Example 2:

    var primes = Tuple.Create(2, 3, 5, 7, 11, 13, 17, 19);
    Console.WriteLine("Prime numbers less than 20: " + 
                      "{0}, {1}, {2}, {3}, {4}, {5}, {6}, and {7}",
                      primes.Item1, primes.Item2, primes.Item3, 
                      primes.Item4, primes.Item5, primes.Item6,
                      primes.Item7, primes.Rest.Item1);

    Example 3 Tuple 数组:

    using System;
    
    public class Example
    {
       public static void Main()
       {
          Tuple<string, Nullable<int>>[] scores = 
                        { new Tuple<string, Nullable<int>>("Jack", 78),
                          new Tuple<string, Nullable<int>>("Abbey", 92), 
                          new Tuple<string, Nullable<int>>("Dave", 88),
                          new Tuple<string, Nullable<int>>("Sam", 91), 
                          new Tuple<string, Nullable<int>>("Ed", null),
                          new Tuple<string, Nullable<int>>("Penelope", 82),
                          new Tuple<string, Nullable<int>>("Linda", 99),
                          new Tuple<string, Nullable<int>>("Judith", 84) };
          int number;
          double mean = ComputeMean(scores, out number);
          Console.WriteLine("Average test score: {0:N2} (n={1})", mean, number);
       }
    
       private static double ComputeMean(Tuple<string, Nullable<int>>[] scores, out int n) 
       {
          n = 0;      
          int sum = 0;
          foreach (var score in scores)
          {
             if (score.Item2.HasValue)
             { 
                n += 1;
                sum += score.Item2.Value;
             }
          }     
          if (n > 0)
             return sum / (double) n;
          else
             return 0;
       }
    }
    // The example displays the following output:
    //       Average test score: 88 (n=7)

    Example 4:

    using System;
    
    public class Class1
    {
       public static void Main()
       {
          int dividend, divisor;
          Tuple<int, int> result;
    
          dividend = 136945; divisor = 178;
          result = IntegerDivide(dividend, divisor);
          if (result != null)
             Console.WriteLine(@"{0}  {1} = {2}, remainder {3}", 
                               dividend, divisor, result.Item1, result.Item2);
          else
             Console.WriteLine(@"{0}  {1} = <Error>", dividend, divisor);
    
          dividend = Int32.MaxValue; divisor = -2073;
          result = IntegerDivide(dividend, divisor);
          if (result != null)
             Console.WriteLine(@"{0}  {1} = {2}, remainder {3}", 
                               dividend, divisor, result.Item1, result.Item2);
          else
             Console.WriteLine(@"{0}  {1} = <Error>", dividend, divisor);
       }
    
       private static Tuple<int, int> IntegerDivide(int dividend, int divisor)
       {
          try {
             int remainder;
             int quotient = Math.DivRem(dividend, divisor, out remainder);
             return new Tuple<int, int>(quotient, remainder);
          }   
          catch (DivideByZeroException) {
             return null;
          }      
       }
    }
    // The example displays the following output:
    //       136945  178 = 769, remainder 63
    //       2147483647  -2073 = -1035930, remainder 757

    我们可以有两个方面的理解,在.NET中关于Tuple我们有如下的定义:

    • 广义上, Tuple就是一种数据结构,通常情况下,其成员的类型及数据是确定的。
    • 狭义上,凡是实现了ITuple接口的类型,都是Tuple的实例。在.NET 4.0 BCL中,预定义了8个Tuple类型。

    在该接口中,定义了一个只读属性Size、两个覆写方法GetHashCode和ToString,实现该接口的Tuple八大金刚如下:

    public class Tuple<T1>
    public class Tuple<T1, T2>
    public class Tuple<T1, T2, T3>
    public class Tuple<T1, T2, T3, T4>
    public class Tuple<T1, T2, T3, T4, T5>
    public class Tuple<T1, T2, T3, T4, T5, T6>
    public class Tuple<T1, T2, T3, T4, T5, T6, T7>
    public class Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>

    在下面的定义中,我们将Custom Request封装为Tuple:

    public class MyRequest
    {
        public Tuple<string, Uri, DateTime> GetMyRequest()
        {
            return Tuple.Create<string, Uri, DateTime>("anytao.com", new Uri("http://anytao.net/"), DateTime.Now);
        }
    }

    为什么要用Tuple呢?这是个值得权衡的问题,上述MyRequest类型中通过3-Tuple对需要的Request信息进行封装,我们当然也可创建一个新的struct来封装,两种方式均可胜任。然则,在实际的编程实践中,很多时候我们需要一种灵活的创建一定数据结构的类型,很多时候新的数据结构充当着“临时”角色,通过大动干戈新类型完全没有必要,而Tuple既是为此种体验而设计的。例如:

    • Point {X, Y},可以表示坐标位置的数据结构。
    • Date {Year, Month, Day},可以表示日期结构;Time {Hour, Minute, Second},可以表示时间结构;而DateTime {Date, Time}则可以实现灵活的日期时间结构。
    • Request {Name, URL, Result},可以表示Request的若干信息。
    • 。。。,随需而取。

    不过,对于Tuple而言,因为其元素数量的有限性,虽然能够满足大部分的需求,当时动态体验是我们越来越期望的编程体验。同时,尤其注意public class Tuple<T1, T2, T3, T4, T5, T6, T7, TRest> 引发的可能ArgumentException,例如:

    var t8 = Tuple.Create<int, int, int, int, int, int, int, int>(1, 2, 3, 4, 5, 6, 7, 8);
    Console.WriteLine(t8.Rest);

    将引发异常:

    Unhandled Exception: System.ArgumentException: The last element of an eight element tuple must be a Tuple.

    提示我们最后的TRest应该为Tuple,所以修改程序为:

    var trest = Tuple.Create<int>(8);
    var t8 = Tuple.Create<int, int, int, int, int, int, int, Tuple<int>>(1, 2, 3, 4, 5, 6, 7, trest);
    Console.WriteLine(t8.Rest);

    则没有任何问题,究其原因我们很容易从Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>构造方法中找到答案:

    public Tuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest)
    {
        if (!(rest is ITuple))
        {
            throw new ArgumentException(Environment.GetResourceString("ArgumentException_TupleLastArgumentNotATuple"));
        }
        this.m_Item1 = item1;
        this.m_Item2 = item2;
        this.m_Item3 = item3;
        this.m_Item4 = item4;
        this.m_Item5 = item5;
        this.m_Item6 = item6;
        this.m_Item7 = item7;
        this.m_Rest = rest;
    }

    TRest类型参数必须被实现为ITuple,否则引发异常。TRest在某种程度上为元素的扩展带来点方便,但是我仔细想来,总觉此处TRest的设计有点多此一举,既然是类型参数,T1、T2、…、TN其实均可为ITuple实例,何必非拘泥于最后一个。

    优略之间

    当前,.NET 4.0预定义的Tuple类型仅有8个,所以我们应考虑对于Tuple提供适度扩展的可能, 然而遗憾的是ITuple类型被实现为internal,所以我们无法继承ITuple,只好自定义类似的实现:

    优势所在:

    • 为方法实现多个返回值体验,这是显然的,Tuple元素都可以作为返回值。
    • 灵活的构建数据结构,符合随要随到的公仆精神。
    • 强类型。
  • 相关阅读:
    逍遥刘强 - 期货大作手风云录(2015年8月28日)
    雷米 - 心理罪:画像(2015年8月17日)
    雷米 - 心理罪:城市之光(2015年8月11日)
    雷米 - 心理罪:暗河(2015年8月9日)
    雷米 - 心理罪:教化场(2015年8月8日)
    付海棠 - 一个农民的亿万传奇(2015年7月14日)
    阿西莫夫 - 神们自己(2015年6月23日)
    张维为 - 中国震撼:一个”文明型国家“的崛起(2015年5月30日)
    华彬 - 华彬讲透孙子兵法(2015年5月22日)
    股峰求道 - 炼股成金:从散户到操盘手的修炼法则(2015年5月11日)
  • 原文地址:https://www.cnblogs.com/xbblogs/p/5435968.html
Copyright © 2011-2022 走看看