程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C#中派生類的方法裡的匿名delegate調用基類的方法會產生無法驗證的代碼(3)

C#中派生類的方法裡的匿名delegate調用基類的方法會產生無法驗證的代碼(3)

編輯:關於C語言

UnifIEd C# 3.0 Specification中,

引用

7.14.4 Outer variables
Any local variable, value parameter, or parameter array whose scope includes the lambda-expression or anonymous-method-expression is called an outer variable of the anonymous function. In an instance function member of a class, the this value is considered a value parameter and is an outer variable of any anonymous function contained within the function member.

7.14.4.1 Captured outer variables
When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated (§5.1.7). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection.

不同語言為閉包分配空間的具體方式不同。C#中,編譯器會將逃逸變量提升為成員變量,並將匿名delegate提升為一個成員方法——不過並不是提升到原本的類中,而是一個由編譯器構造的私有內部類中。上面的代碼,會被編譯器變成類似以下的形式:

引用

C#代碼

public class Bravo : Alpha {
public override void Blah() {
Console.WriteLine("Bravo.Blah");
base.Blah();
}

// compiler generated inner class
private class __locals {
public int __x;
public Bravo __this;
public void __method() {
this.__this.blah();
// on the next line, no such "__nonvirtual__" in C#
__nonvirtual__ ((Alpha)this.__this).Blah());
Console.WriteLine(this.__x);
}
}

public void CharlIE() {
__locals locals = new __locals();
locals.__x = 123;
locals.__this = this;
D d = new D(locals.__method);
d();
}
}

當然這只是偽代碼。C#中並沒有"__nonvirtual__"關鍵字。一般來說,C#中的方法調用都是通過callvirt的IL指令完成的;而通過base關鍵字所做的方法調用則不遵循虛函數要使用最具體版本的規則,因而使用的是call的IL指令來完成。這裡所謂"__nonvirtual__"就是要表現這個意思。

可以看到,原本代碼中匿名delegate裡對base的訪問,實際上被生成到了另外一個類(私有內部類)的方法中,而那個類的"base"其實應該是System.Object……於是就有問題了。關鍵字“base”本來應該只能在同一個繼承系的派生類中使用,這樣生成的代碼就像是讓“base”的作用范圍洩露了一般。沒錯,編譯出來的代碼確實是能運行,卻變得不可驗證(unverifiable)。

但這並不是使用.NET Framework的程序員的錯;他們只是想在正確的地方正確的使用base而已。所以.NET Framework的應對方法是給出一個警告信息,提醒程序員修改代碼來避開這個問題。不幸的是,Mono並沒有提供任何警告提示。用Mono 1.2.5.1編譯上面的代碼,並用.Net Framework的PEVerify來驗證,會看到下面的錯誤信息:

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved