国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

Visual C# 2.0匿名方法揭密

2024-07-21 02:28:26
字體:
供稿:網(wǎng)友
  匿名方法基礎(chǔ)

  匿名方法是c#2.0的一個(gè)新的語言特性。本文的主要內(nèi)容是提供給讀者關(guān)于匿名方法的內(nèi)部實(shí)現(xiàn)和工作方式的一個(gè)更好的理解。本文無意于成為匿名方法的完全語言特性參考。

  匿名方法允許我們定義委托對象可以接受的代碼塊。這個(gè)功能省去我們創(chuàng)建委托時(shí)想要傳遞給一個(gè)委托的小型代碼塊的一個(gè)額外的步驟。它也消除了類代碼中小型方法的混亂。讓我們看看:比方說,我們有一個(gè)字符串集合命名為mycollection。這個(gè)類有一個(gè)方法:獲得集合中滿足用戶提供的過濾準(zhǔn)則的所有項(xiàng),調(diào)用者決定在集合中的一個(gè)特殊項(xiàng)是否符合條件而被檢索到,作為從此方法返回?cái)?shù)組的一部分。

public class mycollection
{
 public delegate bool selectitem(string sitem);
 public string[] getfiltereditemarray(selectitem itemfilter)
 {
  list<string> slist = new list<string>();
  foreach(string sitem in m_slist)
  {
   if (itemfilter(sitem) == true) slist.add(sitem);
  }
  return slist.toarray();
 }

 public list<string> itemlist
 {
  get
  {
   return m_slist;
  }
 }
 private list<string> m_slist = new list<string>();
}

  我們可以用上面定義的類寫如下所示的代碼:

public class program
{
 public static void main(string[] args)
 {
  mycollection objmycol = new mycollection();
  objmycol.itemlist.add("aditya");
  objmycol.itemlist.add("tanu");
  objmycol.itemlist.add("manoj");
  objmycol.itemlist.add("ahan");
  objmycol.itemlist.add("hasi");

  //獲得集合中以字母’a‘開頭的字符項(xiàng)數(shù)組
  string[] astrings = objmycol.getfiltereditemarray(filterstringwitha);
  console.writeline("----- strings starting with letter ''a'' -----");
  foreach(string s in astrings)
  {
   console.writeline(s);
  }
  //獲得集合中以字母’t‘開頭的字符項(xiàng)數(shù)組
  string[] tstrings = objmycol.getfiltereditemarray(filterstringwitht);
  console.writeline("----- strings starting with letter ''t'' -----");
  foreach(string s in tstrings)
  {
   console.writeline(s);
  }
 }

 public static bool filterstringwitha(string sitem)
 {
  if (sitem[0] == ''a'')
   return true;
  else
   return false;
 }
 public static bool filterstringwitht(string sitem)
 {
  if (sitem[0] == ''t'')
   return true;
  else
   return false;
 }
}

  可以看出對于每個(gè)我們想要提供的簡單過濾準(zhǔn)則,我們應(yīng)該定義一個(gè)方法(靜態(tài)或?qū)嵗模_@很快就搞亂了類的代碼。而用匿名方法,代碼變得相當(dāng)自然。下面是這個(gè)program類用匿名方法重寫后的:

public class program
{
 public delegate void mydelegate();
 public static void main(string[] args)
 {
  mycollection objmycol = new mycollection();
  objmycol.itemlist.add("aditya");
  objmycol.itemlist.add("tanu");
  objmycol.itemlist.add("manoj");
  objmycol.itemlist.add("ahan");
  objmycol.itemlist.add("hasi");
  //獲得集合中以字母’a‘開頭的字符項(xiàng)數(shù)組
  string[] astrings = objmycol.getfiltereditemarray(delegate(string sitem)
  {
   if (sitem[0] == ''a'')
    return true;
   else
    return false;
  });
  console.writeline("----- strings starting with letter ''a'' -----");
  foreach (string s in astrings)
  {
   console.writeline(s);
  } //獲得集合中以字母’ t ‘開頭的字符項(xiàng)數(shù)組
  string[] tstrings = objmycol.getfiltereditemarray(delegate(string sitem)
  {
   if (sitem[0] == ''t'')
    return true;
   else
    return false;
  });
  console.writeline("----- strings starting with letter ''t'' -----");
  foreach (string s in tstrings)
  {
   console.writeline(s);
  }
 }
}


  正如上面示例中的所示,我們已能用內(nèi)聯(lián)代碼塊定義的過濾準(zhǔn)則替代定義一個(gè)新的方法來代表每個(gè)過濾準(zhǔn)則。老實(shí)說,用這種內(nèi)聯(lián)代碼可能看起來自然并且避免了定義新方法,但是如果這個(gè)技術(shù)被用于更大的內(nèi)聯(lián)代碼塊,這時(shí)代碼很快變得難于管理并可能導(dǎo)致代碼重復(fù)。因此,使用方法與內(nèi)聯(lián)匿名方法都是委托/事件處理器的可選方案。

  好了,這些就是匿名方法的基礎(chǔ)。本文的余下部分將討論在不同的場景下匿名方法內(nèi)部如何工作的。理解匿名方法如何被實(shí)現(xiàn)和內(nèi)部如何工作對于正確地使用它們是重要的。否則,你使用匿名方法的代碼的結(jié)果看起來將不可預(yù)知。

  匿名方法的靜態(tài)數(shù)據(jù)成員的用法

  匿名方法總是以一個(gè)delegate關(guān)鍵字開始,后面跟著用在方法和方法體(the method body)本身中的參數(shù)。正如從上面示例中所見,用戶不需要確定匿名方法的返回類型。它(譯注:指返回類型)由方法體中的return語句推斷而來。.net clr不能執(zhí)行像匿名方法一樣的自由流(free flowing)代碼塊。clr要求:它執(zhí)行的每個(gè)方法是一個(gè)類型的一部分,并且應(yīng)該是一個(gè)靜態(tài)(static)方法或?qū)嵗╥nstance)方法(譯注:若一個(gè)方法聲明中含有 static 修飾符,則稱該方法為靜態(tài)方法。若其中沒有 static 修飾符時(shí),則稱該方法為實(shí)例方法。靜態(tài)方法不對特定實(shí)例進(jìn)行操作,在靜態(tài)方法中引用 this 是編譯時(shí)錯(cuò)誤。實(shí)例方法對類的某個(gè)給定的實(shí)例進(jìn)行操作,而且可以用 this來訪問該實(shí)例)。因此當(dāng)你在一個(gè)類的代碼中寫匿名方法并編譯這個(gè)代碼時(shí),c#編譯器默默地在你定義匿名方法的相同的類中創(chuàng)建了一個(gè)靜態(tài)或?qū)嵗椒āK阅涿椒ㄖ皇且粋€(gè)在類中定義你自己方法以傳遞到委托(委托處理器/事件處理器)的方便的語法。

  當(dāng)你編譯上面的示例時(shí),c#編譯器在類''program''內(nèi)部即我們定義匿名方法的地方創(chuàng)建了兩個(gè)private static方法。它此時(shí)用這些static方法的地址取代了匿名方法。編譯器決定如何創(chuàng)建靜態(tài)方法或?qū)嵗椒ㄈQ于匿名方法被定義的類中的靜態(tài)或?qū)嵗龜?shù)據(jù)成員的用法。在我們的示例中,我們沒有用到任何類''program''的數(shù)據(jù)成員,因?yàn)檎{(diào)用一個(gè)靜態(tài)方法而不是一個(gè)實(shí)例方法將是高效的,因此c#編譯器創(chuàng)建一個(gè)static方法來封裝我們的匿名方法的代碼。下面是這個(gè)示例程序集''program'' 類的ildasm視圖。高亮部分顯示了由c#編譯器默默添加到''program''類的新的靜態(tài)方法。


  如果我們已經(jīng)使用了用匿名方法的''program'' 類的任何靜態(tài)數(shù)據(jù),c#編譯器將仍然在''program'' 類里創(chuàng)建一個(gè)靜態(tài)方法來包裝匿名方法。

  匿名方法的實(shí)例數(shù)據(jù)成員用法

  讓我們在我們的示例中的''program''類中定義一個(gè)新的實(shí)例方法,并使用示例類(譯注:即''program''類)一個(gè)實(shí)例數(shù)據(jù)成員。下面的代碼顯示了修改后的示例:

public class program
{
 public delegate void mydelegate();
 public static void main(string[] args)
 {
  //實(shí)例數(shù)據(jù)成員測試
  program p = new program();
  for(int i=1;i<=5;i++)
   p.testinstancedatamembers();
 }
 public void testinstancedatamembers()
 {
  mydelegate d = delegate
  {
   console.writeline("count: {0}",++m_icount);
  };
  d();
 }
 public int m_icount = 0;
}

  我們定義了一個(gè)新的實(shí)例方法:testinstancedatamembers,在''program''類中這個(gè)方法定義了一個(gè)匿名方法,匿名方法使用了實(shí)例數(shù)據(jù)成員:隸屬''program''類的m_icount。當(dāng)這個(gè)示例編譯時(shí),c#編譯器將創(chuàng)建一個(gè)private實(shí)例方法來包裝這個(gè)在testinstancedatamembers中定義的匿名方法。c#編譯器必須創(chuàng)建一個(gè)實(shí)例方法因?yàn)樵摲椒ㄐ枰L問''program''類的實(shí)例數(shù)據(jù)成員。下面是這個(gè)示例程序集''program''類的ildasm視圖。在圖的下部選中部分顯示了由c#編譯器默默添加到''program''類的新的private實(shí)例方法。

  匿名方法的局部變量用法

  到現(xiàn)在為止,我們對匿名方法如何工作以及內(nèi)部如何實(shí)現(xiàn)有了一點(diǎn)基本的理解。從根本上說,c#創(chuàng)建了private方法來包裝匿名方法。同時(shí)這些方法的簽名與它們被分配到的委托相匹配。現(xiàn)在,讓我們看看下面的代碼:

public class program
{
 public delegate void mydelegate();
 public static void main(string[] args)
 {
  int itemp = 100;
  mydelegate dlg = delegate
  {
   console.writeline(itemp);
  };
  dlg();
 }
}

  對于我們到現(xiàn)在為止對匿名方法已了解的內(nèi)容來說,這段代碼不應(yīng)該編譯。因?yàn)槲覀儧]有使用如何實(shí)例數(shù)據(jù)成員,c#編譯器應(yīng)該在''program''類中創(chuàng)建一個(gè)private靜態(tài)方法來包裝這個(gè)匿名方法。但是新的方法如何訪問局部變量呢?這讓我們相信該代碼將不能被編譯。但是令人驚訝的是,c#編譯器成功編譯了這個(gè)代碼而沒有任何錯(cuò)誤或報(bào)警。而且,當(dāng)你執(zhí)行這個(gè)示例時(shí),在控制臺屏幕上輸出打印出itemp變量的正確的值。現(xiàn)在讓我們進(jìn)入匿名方法的高級話題。一個(gè)匿名方法有封裝在其方法體中使用了的環(huán)境變量的值的能力。這個(gè)封裝應(yīng)用于匿名方法被定義的方法中的所有局部變量。當(dāng)c#編譯器在一個(gè)匿名方法的方法體中識別出用到一個(gè)局部變量,它就會做如下事情:

  1. 創(chuàng)建一個(gè)新的private類作為匿名方法被定義的類的一個(gè)內(nèi)部類。

  2. 在新類(譯注:即內(nèi)部類)中創(chuàng)建一個(gè)公共數(shù)據(jù)成員,使用與用在匿名方法體中的局部變量相同的類型和名稱。

  3. 在包裝匿名方法的新類中創(chuàng)建一個(gè)public實(shí)例方法。

  4. 用新類中的聲明替代局部變量的聲明。創(chuàng)建該新類的一個(gè)實(shí)例代替局部變量的聲明。

  5. 用新類實(shí)例的數(shù)據(jù)成員替代在匿名方法體內(nèi)部和外部使用的局部變量。

  6. 用在新類中定義的實(shí)例方法的地址取代匿名方法的定義。

  因此在編譯時(shí),上面的代碼將被c#編譯器翻譯為如下代碼:

public class program
{
 private class innerclass
 {
  private void instancemethod()
  {
   console.writeline(itemp);
  }
  public int itemp;
 }
 public delegate void mydelegate();
 public static void main(string[] args)
 {
  innerclass localobject = new innerclass();
  localobject.itemp = 100;
  mydelegate dlg = new mydelegate(localobject.instancemethod);
  dlg();
 }
}

  正如上面的偽代碼所示,c#編譯器為''program''類生成了一個(gè)private內(nèi)部類。在匿名方法中使用的局部變量作為新的已創(chuàng)建的內(nèi)部類的一個(gè)實(shí)例數(shù)據(jù)成員而捕獲。并且匿名方法本身被包裝在內(nèi)部類的實(shí)例方法中。最后,該實(shí)例方法在main方法中作為一個(gè)委托處理器而使用。這樣,當(dāng)委托被調(diào)用時(shí),對于在被封裝入匿名方法中的局部變量將會有一個(gè)正確的值。下面圖中選定的部分顯示了由c#編譯器默默添加到''program'' 類的新的private內(nèi)部類。


  被用在匿名方法中的局部變量有著超出用到它們的外部常規(guī)方法的生命周期。這個(gè)技術(shù),在其它語言中,就是大家都知道的closures。除去匿名方法提供的簡單語法,closures是匿名方法提供給開發(fā)者的一個(gè)功能強(qiáng)大的技術(shù)。該技術(shù)允許委托處理器代碼(匿名方法)訪問在常規(guī)方法內(nèi)部被定義的局部變量。這就允許out-of-band數(shù)據(jù),除了委托參數(shù)之外還有數(shù)據(jù)將被傳遞到委托,以供在其方法執(zhí)行時(shí)使用。沒有這個(gè)技術(shù),每個(gè)委托和其相應(yīng)的處理器方法就不得不聲明表示局部上下文數(shù)據(jù)的參數(shù),隨著時(shí)間的過去這(譯注:指不斷聲明表示局部上下文數(shù)據(jù)的參數(shù))將變得難于管理。 匿名方法的作用域和局部變量用法

  我們討論了在方法的主作用域(the main scope)中的匿名方法的實(shí)現(xiàn)。當(dāng)一個(gè)匿名方法在一個(gè)嵌套作用域中被定義時(shí),并且匿名方法中用到獨(dú)立作用域級的局部變量,c#為每個(gè)作用域創(chuàng)建一個(gè)private內(nèi)部類。比如,假設(shè)scope 1有局部變量itemp,而scope 2,是scope 1的嵌套作用域,有一個(gè)局部變量jtemp。讓在使用來自scope 1 和 scope 2局部變量itemp 和 jtemp的 scope 2中,我們定義一個(gè)匿名方法。下面的代碼顯示了上面描述的示例:

public class program
{
 public delegate void mydelegate();
 public static void main(string[] args)
 {
  mydelegate dlg = null;
  int itemp = 100;
  if (itemp > 50)
  {
   int jtemp = 200;
   dlg = delegate
   {
    console.writeline("itemp: {0}, jtemp: {1}",itemp,jtemp);
   };
  }
  dlg();
 }
}

  當(dāng)上面的代碼被編譯時(shí),c#編譯器在''program''類中創(chuàng)建兩個(gè)內(nèi)部類。一個(gè)內(nèi)部類包裝局部變量itemp作為一個(gè)public數(shù)據(jù)成員。第二個(gè)內(nèi)部類包裝在嵌套作用域中的局部變量,jtemp,作為一個(gè)public數(shù)據(jù)成員,同時(shí)在相同的嵌套作用域中包裝匿名方法作為public實(shí)例方法。c#編譯器為上面的代碼生成下面的偽代碼:

public class program
{
 //包裝來自外部作用域的局部變量''itemp''的類
 private class innerclassscope1
 {
  public int itemp;
 }
 //包裝來自內(nèi)部作用域和匿名方法的局部變量的類
 private class innerclassscope2
 {
  public void instancemethod()
  {
   console.writeline("itemp: {0}, jtemp: {1}", localobjectscope1.itemp, jtemp);
  }
  public innerclassscope1 localobjectscope1;
  public int jtemp;
 }
 public delegate void mydelegate();
 public static void main(string[] args)
 {
  mydelegate dlg = null;
  innerclassscope1 localobject1 = new innerclassscope1();
  localobject1.itemp = 100;
  if (localobject1.itemp > 50)
  {
   innerclassscope2 localobject2 = new innerclassscope2();
   localobject2.localobjectscope1 = localobject1;
   localobject2.jtemp = 200;
   dlg = new mydelegate(localobject2.instancemethod);
  }
  dlg();
 }
}

  正如上面的代碼所示,包裝匿名方法的內(nèi)部類將擁有所有代表外部作用域局部變量的對象,這些變量被用在匿名方法中,像public數(shù)據(jù)成員。下圖顯示了c#默默創(chuàng)建的內(nèi)部類的ildasm視圖:

  在循環(huán)控制結(jié)構(gòu)內(nèi)使用匿名方法的局部變量的用法

  當(dāng)處理循環(huán)控制結(jié)構(gòu)時(shí)將局部變量封裝入類的數(shù)據(jù)成員有著有趣但危險(xiǎn)的一面,讓我們看看下面代碼:

public class program
{
 public delegate void mydelegate();
 public static void main(string[] args)
 {
  mydelegate d = null;
  for (int i = 1; i <= 5; i++)
  {
   mydelegate tempd = delegate
   {
    console.writeline(i);
   };
   d += tempd;
  }
  d();
 }
}

  上面的代碼運(yùn)行時(shí)將會有什么輸出呢?我們的意圖是捕獲在我們的匿名方法中的循環(huán)計(jì)數(shù)變量''i''并顯示之。我們預(yù)期的輸出應(yīng)該如下所示:

  1
  2
  3
  4
  5

  但是如果你運(yùn)行上面的代碼,輸出將是如下所示:
 
  6
  6
  6
  6
  6

  如果我們仔細(xì)回憶我們關(guān)于匿名方法的內(nèi)部工作機(jī)制的知識,我提到:在匿名方法中被捕獲的任何局部變量將會被該作用域的一個(gè)新的已創(chuàng)建內(nèi)部類的實(shí)例數(shù)據(jù)成員替代。對于循環(huán)控制變量,作用域是包含了for循環(huán)的作用域,這就是上面的簡單代碼所示的main方法體。因此當(dāng)該代碼編譯時(shí),c#編譯器生成創(chuàng)建了內(nèi)部類的實(shí)例的代碼,包裝了匿名方法和循環(huán)計(jì)數(shù)變量,在for循環(huán)的外部。并且該內(nèi)部類的實(shí)例的數(shù)據(jù)成員,代表了循環(huán)計(jì)數(shù)變量,將被用來替代用于for循環(huán)而且也在匿名方法中使用的原始循環(huán)計(jì)數(shù)變量。因此來自內(nèi)部類的相同實(shí)例的數(shù)據(jù)成員被用于for循環(huán)并且也用在包裝匿名方法的實(shí)例方法中。作為循環(huán)完成時(shí)的結(jié)果,實(shí)例數(shù)據(jù)成員會增加六次。這里有一個(gè)需要注意的重要地方:盡管這個(gè)循環(huán)在五次迭代后結(jié)束,在它跳出循環(huán)控制結(jié)構(gòu)時(shí)循環(huán)計(jì)數(shù)變量被增加了六次。既然該循環(huán)控制變量是一個(gè)實(shí)例數(shù)據(jù)成員,第六次增加觸發(fā)了已由循環(huán)計(jì)數(shù)變量提供的循環(huán)結(jié)束條件。既然相同實(shí)例的一個(gè)方法被用做匿名方法的委托處理器,在委托結(jié)束時(shí)被調(diào)用,所有委托的實(shí)例將被指向相同實(shí)例,同時(shí)將為數(shù)據(jù)成員顯示相同值,就是6。這就是我在本節(jié)開始已提到過的有危險(xiǎn)影響的一面。

  為了克服這個(gè)問題并獲得預(yù)期的結(jié)果,匿名方法應(yīng)該在for循環(huán)的作用域中捕獲一個(gè)局部變量,它將有與循環(huán)計(jì)數(shù)變量的相同的值。這可以通過如下修改示例代碼獲得:

public class program
{
 public delegate void mydelegate();
 public static void main(string[] args)
 {
  mydelegate d = null;
  for (int i = 1; i <= 5; i++)
  {
   int k = i;
   mydelegate tempd = delegate
   {
    console.writeline(k);
   };
   d += tempd;
  }
  d();
 }
}

  在你運(yùn)行上面的代碼示例時(shí),將會獲得預(yù)期的輸出,也就是:

  1
  2
  3
  4
  5

  原因就是,c#編譯器將為for循環(huán)的每次迭代而包裝局部變量''k''的內(nèi)部類創(chuàng)建 實(shí)例。同時(shí)包裝了每個(gè)循環(huán)迭代的實(shí)例上的匿名方法的這個(gè)方法被用做一個(gè)委托處理器。

  總結(jié)

  匿名方法是c#2.0語言增加的一個(gè)非常有用和強(qiáng)大的功能。除了介紹的一些對委托聲明和用法上的語法改進(jìn),microsoft已在使匿名方法代碼自然融入所包含的方法體方面獲得很大進(jìn)展,包括訪問在包含(匿名方法)的方法定義的作用域中的局部變量。最后,我希望本文提供給c#開發(fā)人員正確而聰明地利用匿名方法的必備知識。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 浦城县| 康平县| 彭水| 喀什市| 揭阳市| 陕西省| 武山县| 昌邑市| 红安县| 运城市| 慈利县| 冷水江市| 乐至县| 富民县| 正安县| 遂平县| 东丰县| 冕宁县| 伊通| 浮梁县| 台北县| 临澧县| 南投县| 杭锦后旗| 新平| 五大连池市| 广南县| 南漳县| 汉寿县| 扎兰屯市| 东乌| 留坝县| 石首市| 阜城县| 陇南市| 乌兰浩特市| 扎囊县| 成都市| 壤塘县| 阜阳市| 乌什县|