1. 程式人生 > >【C#復習總結】匿名類型由來

【C#復習總結】匿名類型由來

數據類型 over 無效 訪問性 屬性。 知乎 私有 不能 默認構造函數

1 屬性

這得先從屬性開始說,為什麽外部代碼訪問對象內部的數據用屬性而不是直接訪問呢,這樣豈不是更方便一些,但是事實證明直接訪問是不安全的。那麽,Anders Hejlsberg(安德斯·海爾斯伯格)就為C#加入了屬性這種語法糖,用起來跟數據成員一樣,但實際上是 setXX()getXX(),既安全又方便。

屬性:是訪問對象的首選方式,因為它們禁止外部代碼訪問對象內部的數據存儲機制的實現。

public int MyIntProp
{
    get
    {
    //property get code
    }
    set
    {
    
//Proerty set code } }

1.1 get關鍵字

get塊必須有一個屬性的返回值,簡單的屬性一般與私有字段相關聯,以控制對這個字段的訪問,此時get塊可以直接返回該字段的值,例如:

private int myInt;

public int myIntProp
{
  get
  {
    return myInt;
  }
  set
  {
    //Property set code.
  }
}

類外部的代碼不能直接訪問這個myInt字段,私有的,必須使用屬性來訪問該字段。

1.2 set關鍵字

set函數以類似的方法把一個值賦給字段。這裏使用

value表示用戶提供的屬性值:

private int myInt;
public int myIntProp
{
  get 
  {
    return myInt;
  }
  set
  {
    myInt = value;
  }
}

value等於類似與屬性相同的值,所以如果屬性和字段使用相同的類型,就不必擔心數據類型轉換了。

這個簡單的屬性只能直接訪問myInt字段。在對操作進行更多的控制的時候,屬性的真正作用才能發揮出來,例如,使用下面的代碼實現set塊:

set
{
  if(value >= 0 && value <= 10
)   myInt = value; }

只用賦給屬性的值在1~10之間,才會改myInt。此時,要做一個重要的設計選擇:如果使用了無效值,該怎麽辦:

  • 什麽也不做
  • 給字段賦默認值
  • 繼續執行,就好像沒有發生錯誤一樣,但記錄下來該事件,以備將來分析
  • 拋出異常

一般情況下,後面兩個選擇效果較好,選擇哪個選項取決於如何使用類,以及給用戶授予多少控制權。拋出異常給用戶提供的控制權相當的大,例如:

set
{
  if(value >= 0 && value <= 10)
    myInt = value;
  else
    throw (new ArgumentOutOfRangeException("myIntProp",value,"myIntProp must be assigned a value between 0 and 10."))
}

這可以在使用屬性的代碼中通過try...catch...finaly邏輯來處理。

註:屬性可以使用virtualoverrideabstract關鍵字,就像方法一樣,但這幾個關鍵字不能用於字段。最後,如上述,訪問器可以有自己的訪問性。

實例:

public class MyClass
{
public readonly string Name;
private int intVal;

public int Val
{
  get
  {
  return intVal;
  }
  set
  {
    if (value >= 0 && value <= 10 )
      intVal = value;
    else
      throw (new ArgumentOutOfRangeException("Val",value,"Val must be assigned a value between 0 ang 10."));
  }
}
public override string ToString()
{
  return "Name:"+Name+"\nVal:"+Val;
}
private MyClass(): this("Default Name")
{

}
public MyClass(string newName)
{
  Name = newName;
  intVal = 0;
}
}

static void Main(string[] args)
{
  Console.WriteLine("Creating object myobj...");
  MyClass myObj = new MyClass("My Object");
  Console.WriteLine("myObj created.");
  for (int i = -1; i <= 0; i++ )
  {
    try
    {
      Console.WriteLine("\nAttempting to assign {0} to myObj.val...",i);
      myObj.Val = i;
      Console.WriteLine("Value {0} assigned to myObj.val.", myObj.Val);
    }
    catch(Exception e)
    {
      Console.WriteLine("Exception {0} throw.",e.GetType().FullName);
      Console.WriteLine("Message:\n\"{0}\"",e.Message);
    }
  }
  Console.WriteLine("\nOutputting myObj.ToString()...");
  Console.WriteLine(myObj.ToString());
  Console.WriteLine("myObj.ToString() Output.");
  Console.ReadKey();
}

Main()中的的代碼創建並使用在MyClass.cs中定義的MyClass類的實例。實例化這個類必須使用非默認的構造函數來進行,因為MyClass類的默認構造函數是私有的。

Main()試著給myObjMyClass的實例)的Val屬性賦值。for循環在兩次中賦值-10try..catch...結構用於檢測拋出的異常。把-1賦給屬性時,會拋出System.ArgumentOutOfException類型的異常,catch塊中的代碼會把改異常的信息輸出到控制臺窗口中。在下一個循環中,值0成功的賦給了Val屬性,通過這個屬性再把值賦給私有字段intVal

2 自動屬性

但是呢,安德斯還是覺得代碼太多,還應該在優化一下,就想出了自動屬性。

自動屬性。利用自動屬性,可以用簡化的語法聲明屬性,C#編譯器會自動添加未鍵入的內容,具體而言,編譯器會聲明一個用於存儲屬性的私有字段,並在屬性的getset塊中使用該字段(非常貼心),我們無需考慮細節。

public int MyIntProp

{
  get;
  set;
}

我們按照通常的方式定義屬性的可訪問性、類型和名稱。但是沒有給getset塊提供實現的代碼。這些塊的實現代碼(和底層的字段)由編譯器提供。

使用自動屬性時,只能通過屬性訪問數據,不能通過底層的私有字段來訪問,我們不知道底層私有字段的名稱(該名稱是編譯期間定義的)。但這並不是一個真正意義上的限制,因為可以直接使用屬性名。自動屬性的唯一限制是他們必須包含getset存儲器,無法使用這種方法定義只讀和只寫屬性。

3 對象初始化器

對象初始化器定義:初始化器分為對象初始化器和集合初始化器,此處指我們講的是對象初始化器,

作用:用較少的代碼創建一個新對象並為對象的若幹屬性和公共數據成員進行賦值。

談到初始化器,先談一下構造函數,構造從表面意思就知道這是用來構建類的(當然初始化一些成員也是屬於構建的範圍,但還有其他作用)

對象初始化過程:先定義類的屬性,再實例化和初始化這個類。

定義類的屬性用自動屬性來定義,實例化和初始化這個類的一個對象實例就必須用C#裏面的默認的無參構造函數來實現下段代碼。

先看一個類定義:

public class Curry
{
    public string MainIngredient { get; set; }
    public string Style { get; set; }
    public int Spiciness { get; set; }
}

這個類有3 個屬性,用自動屬性語法來定義。如果希望實例化和初始化這個類的一個對象實例,就必須執行如下幾個語句:

  1. 第一種方式
Curry tastyCurry = new Curry();

tastyCurry.MainIngredient = "panir tikka";

tastyCurry.Style = "jalfrezi";

tastyCurry.Spiciness = 8;

如果類定義中未包含構造函數,這段代碼就使用C#編譯器提供的默認無參數構造函數。這種方式還是有點復雜,應該還有比這個簡單的,你猜對了。

2.第二種方式(去掉括號)

Curry tastyCurry=new Curry
{
    tastyCurry.MainIngredient = "panir tikka",

    tastyCurry.Style = "jalfrezi",

    tastyCurry.Spiciness = 8,
}

然後問題就來了,如果你有10個數據成員,實例化和初始化這個類的一個對象實例要寫多少個?為了簡化這個過程,安德斯又機靈了一下想到了更高級的方式,采用一個合適的非默認構造函數。

如下:

Class tastyCurry =new Curry(“panir tikka ”, “jalfrezi”,8);

這段代碼工作的很好,它會強制使用Curry類的代碼使用這個構造函數,這將阻止前面默認使用無參構造函數的代碼運行。

4 匿名類型

你以為這樣就非常方便了麽,只能說你太年輕,天外有天,人外有人,看看我們的題目,對了,就是它,我們的主角,匿名類型。

匿名類型提供了一種方便的方法,可用來將一組只讀屬性封裝到單個對象中,而無需首先顯式定義一個類型。 類型名由編譯器生成,並且不能在源代碼級使用。 每個屬性的類型由編譯器推斷。可通過使用 new 運算符和對象初始值創建匿名類型。

來,我們舉個栗子。

以下示例顯示了用兩個名為 Amount Message 的屬性進行初始化的匿名類型。

var v = new { MainIngredient =“panir tikka ”, Style =“jalfrezi” Spiciness=8};  

Console.WriteLine(v.MainIngredient + v.Style+v.Spiciness);  

看見了麽,對,沒錯,就是這麽簡單。

備註:關於C# 的匿名類型為什麽要限制屬性為只讀呢?

來自知乎網友的一段話我覺得說的挺好的。

其實匿名類型是C# 3.0引入的,C# 3.0引入的所有新特性基本都是為了實現LINQ這一偉大的語言特性。匿名類型是為了解決LINQ中選擇部分字段以及多字段作為分組依據聚合或是多字段聯接的問題的。所以,說白了匿名類型設計的目標就是元組。匿名類型本質上就是關系模型中的元組在C#裏面的映射。元組顯然是不可變的,匿名類型也沒有必要設計成可變的來自找麻煩。

至此,匿名類型的由來就大致講清楚了,主要是因為工程師要簡便優化代碼,匠心創作,匿名類型由此誕生。

參考文獻:【c#入門經典第五版】【知乎】

技術分享圖片

友情提示

作者: mhq_martin

博客園地址: http://www.cnblogs.com/mhq-martin/

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。

【C#復習總結】匿名類型由來