[你必須知道的.NET]第三十二回,深入.NET 4.0之,Tuple一二

作者: Anytao  來源: 博客園  發布時間: 2009-06-01 10:13  閱讀: 4980 次  推薦: 0   原文鏈接   [收藏]  
摘要:Tuple,是函數式編程的概念之一,早見于Elang、F#等動態語言。不過,我第一次聽說Tuple還早在2005年園子的Ninputer大牛提出在.NET 2.0實現Tuple的基本想法,我們可以通過以下地址仰慕當時的歷史片段:

系列文章導航:

[你必須知道的.NET] 開篇有益

[你必須知道的.NET] 第一回:恩怨情仇:is和as

[你必須知道的.NET] 第二回:對抽象編程:接口和抽象類

[你必須知道的.NET] 第三回:歷史糾葛:特性和屬性

[你必須知道的.NET] 第四回:后來居上:class和struct

[你必須知道的.NET] 第五回:深入淺出關鍵字---把new說透

[你必須知道的.NET] 第六回:深入淺出關鍵字---base和this

[你必須知道的.NET] 第七回:品味類型---從通用類型系統開始

[你必須知道的.NET] 第八回:品味類型---值類型與引用類型(上)-內存有理

[你必須知道的.NET] 第九回:品味類型---值類型與引用類型(中)-規則無邊

[你必須知道的.NET] 第十回:品味類型---值類型與引用類型(下)-應用征途

[你必須知道的.NET] 第十一回:參數之惑---傳遞的藝術(上)

[你必須知道的.NET] 第十二回:參數之惑---傳遞的藝術(下)

[你必須知道的.NET] 第十三回:從Hello, world開始認識IL

[你必須知道的.NET] 第十四回:認識IL代碼---從開始到現在

[你必須知道的.NET] 第十五回:繼承本質論

[你必須知道的.NET] 第十六回:深入淺出關鍵字---using全接觸

[你必須知道的.NET] 第十七回:貌合神離:覆寫和重載

[你必須知道的.NET] 第十八回:對象創建始末(上)

[你必須知道的.NET] 第十九回:對象創建始末(下)

[你必須知道的.NET]第二十回:學習方法論

[你必須知道的.NET]第二十一回:認識全面的null

[你必須知道的.NET]第二十二回:字符串駐留(上)---帶著問題思考

[你必須知道的.NET]第三十二回,深入.NET 4.0之,Tuple一二

 

Tuple,是函數式編程的概念之一,早見于Elang、F#等動態語言。不過,我第一次聽說Tuple還早在2005年園子的Ninputer大牛提出在.NET 2.0實現Tuple的基本想法,我們可以通過以下地址仰慕當時的歷史片段:
探討.NET 2.0中Tuple的實現方法
由此可見,Tuple不是.NET 4.0的創造發明,但卻是C#趨于函數式編程概念的必要補充。那么,我們首先來看看,什么是Tuple?

Tuple為何物?

什么是Tuple,在漢語上我們將其翻譯為元組。Tuple的概念源于數學概念,表示有序的數據集合。在.NET中Tuple被實現為泛型類型,n-Tuple表示有n個元素的Tuple,集合的元素可以是任何類型,例如定義一個3-Tuple表示Date(Year, Month, Day)時可以定義為:

// Release : code01, 2009/05/29
// Author  : Anytao, http://www.anytao.com
var date = Tuple.Create<int, int, int>(2009, 5, 29);

通過Tuple.Create將定義一個Tuple實例,該實例實現三個數據成員:

o_anytao-insidenet-32-01[1]

對于Tuple的具體解析我們隨后分析,當下僅了解一個大致。

我們可以有兩個方面的理解,在.NET中關于Tuple我們有如下的定義:

  • 廣義上, Tuple就是一種數據結構,通常情況下,其成員的類型及數據是確定的。
  • 狹義上,凡是實現了ITuple接口的類型,都是Tuple的實例。在.NET 4.0 BCL中,預定義了8個Tuple類型。例如最簡單的Tuple定義為:
public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITuple
{
}

其他所有的Tuple類型都實現了ITuple接口,該接口被定義為:

interface ITuple
{
    int Size { get; }

    int GetHashCode(IEqualityComparer comparer);
    string ToString(StringBuilder sb);
}

在該接口中,定義了一個只讀屬性Size、兩個覆寫方法GetHashCode和ToString,實現該接口的Tuple八大金剛如下:

public class Tuple
public class Tuple
public class Tuple
public class Tuple
public class Tuple
public class Tuple
public class Tuple
public class Tuple

注:Size屬性、ToString(StringBuilder sb)方法,均被實現為顯示接口方法,所以只能以接口實例訪問,不過ITuple本身被定義internal,意味著我們無法在程序中訪問ITuple,何意何解尚不明確。

在下面的定義中,我們將Custom Request封裝為Tuple:

// Release : code02, 2009/05/29
// Author  : Anytao, http://www.anytao.com
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 inside

為了對Tuple一探究竟,我們使用Reflector工具打開神秘之門,就實現而言,Tuple類型略顯單薄,并沒有什么“神奇”的設計,以Tuple而言,我們可以看到其部分實現:

[Serializable]
public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITuple
{
    // Fields
    private T1 m_Item1;
    private T2 m_Item2;

    // Methods
    public Tuple(T1 item1, T2 item2)
    {
        this.m_Item1 = item1;
        this.m_Item2 = item2;
    }

    string ITuple.ToString(StringBuilder sb)
    {
        sb.Append(this.m_Item1);
        sb.Append(", ");
        sb.Append(this.m_Item2);
        sb.Append(")");
        return sb.ToString();
    }
    int ITuple.Size
    {
        get
        {
            return 2;
        }
    }
    // Properties
    public T1 Item1
    {
        get
        {
            return this.m_Item1;
        }
    }
    public T2 Item2
    {
        get
        {
            return this.m_Item2;
        }
    }
    //More and more...
}

其他的Tuple類型也大致如此,所以我們易于知曉Item1、Item2、…、ItemN是如何被定義的,同時也納悶Size屬性將何去何從,也打消了我們期望通過foreach來遍歷Tuple元素的可能,未來如何,只有期待。

不過,對于Tuple而言,因為其元素數量的有限性,雖然能夠滿足大部分的需求,當時動態體驗是我們越來越期望的編程體驗。同時,尤其注意public class Tuple 引發的可能ArgumentException,例如:

// Release : code03, 2009/05/31
// Author  : Anytao, http://www.anytao.com
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,所以修改程序為:

// Release : code04, 2009/05/31
// Author  : Anytao, http://www.anytao.com
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構造方法中找到答案:

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_TupleLastArgument
NotATuple"));
    }
    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元素都可以作為返回值。
  • 靈活的構建數據結構,符合隨要隨到的公仆精神。
  • 強類型。

不足總結:

  • 當前Tuple類型的成員被實現為確定值,目前而言,還沒有動態決議成員數量的機制,如果你有可以告訴我:-)
  • public class Tuple,可能引發ArgumentException
0
0
 
標簽:.NET 4.0 Tuple
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()