漫談.Net關鍵字系列之一Sealed與Final

作者: Aicken(李鳴)  來源: 博客園  發布時間: 2010-09-09 21:30  閱讀: 9233 次  推薦: 4   原文鏈接   [收藏]  
摘要:Sealed與Final修飾符其實并不是一個語言平臺的產物,他們有著各自所屬的語言環境,但這兩個關鍵字都是.Net平臺中不可或缺的,那么二者用法幾何,隨本文一探究竟。

      Sealed與Final修飾符其實并不是一個語言平臺的產物,他們有著各自所屬的語言環境,但這兩個關鍵字都是.Net平臺中不可或缺的,那么二者用法幾何,隨本文一探究竟。

  一.Sealed

      sealed 修飾符可以應用于類、實例方法和屬性。用于類時,該類被稱為密封類,密封類不能被繼承;用于方法時,該方法被稱為密封方法,密封方法會重寫基類中的方法;sealed修飾符應用于方法或屬性時,必須始終與override一起使用;結構是隱式密封的,因此它們不能被繼承。

    ● 描述方法:

 
//Error: cannot be sealed because it is not an override
public sealed string func()
{

return "";
}


//OK
public sealed override string func()
{

return "";
}

   ● 繼承中的方法:(TestChild2中無法重寫任何方法)

clip_image002

    ●描述屬性:

 
public sealed override double Hours
{

get { return 0.1; }
set { }
}

  ●描述變量:

 
//Error The modifier 'sealed' is not valid for this item
sealed override string a;

  ●描述接口:

 
interface Itesta
{

//Error cannot be sealed because it is not an override
sealed string Geta();
}

  ● sealed能提高性能優化?

  有一些朋友認為當元素被標記為sealed時,有助于系統運行性能的提升,其理由有2:

      1.有助于JIT內聯。

      2.消除了協變與逆變和后期綁定,使CLR直接執行這個實例。

      這看似是有些道理的,可這樣做又會提升多少性能,提升性能的同時又損失了什么呢?

      先說說“第1點”,促使JIT內聯代碼的因素有很多,JIT不會因為一個類是sealed,就去裝入其中的內容(詳見.Net Discovery 系列之六--深入淺出.Net實時編譯機制(下),這也不符合程序局部性原理。http://www.cnblogs.com/isline/archive/2009/12/27/1633453.html),而對于sealed類內部方法的內聯,很大原因也是由于方法槽映射關系決定的,sealed作用有待考證。

      第2點,由于sealed類不可派生或被繼承,所以的確在運行時省去可CLR一些額外的工作,但是這些工作只是一些類似于“尋址”的工作,因為虛擬方法表已經完成了運行時與編譯時的對應關系,純粹的運行時只是在尋找這些關系而已,所以sealed省去的只是一部分較為復雜的尋址關系,因為即使沒有繼承,也不可避免一個方法表的應用。

      而這樣做又損失了什么呢?大家想想,面向對象的原因是什么?是提升性能嗎?顯然不是,面向對象的只是高級語言層面的,最終運行的代碼都是以順序流程的方式出現,面向對象的本質是“抽象”,它解決的是軟件產品的“控制”問題,變不可控為可控,變不可預測的風險為可預測的風險,所以如果因為要提升性能,而把大部分類都sealed化,豈不是大大削弱了面向對象的抽象能力呢?

      ● Sealed不能同時abstract?

      也許在高級語言中抽象須實現與密封不可繼承是一對矛盾者,但IL暴露了一些不一樣的細節,讓我們來分析一下這段IL代碼:

clip_image004

      這段代碼簡單得很,就是聲明了一個類,然而這個類卻是abstract和Sealed的,猜猜這個類用了什么修飾符修飾它?

好吧,其實高級語言中對應的修飾符就是static。

clip_image005

      static類不能被實例化(abstract)亦不可派生(Sealed),我想abstract同時Sealed也未嘗不可,但這樣做會使語義出現二義性,為避免這種效果才規定在編輯器中不可abstract+Sealed,static修飾類的初衷我想也是如此,實際上static在修飾類時,就是一個包含了實現的abstract+Sealed的類,這個類不能被實例化也不能派生出新的類。

  二.Final

      final修飾符來限定變量、字段、方法和類。用于變量時,該變量只能賦值一次,不可修改;用于方法時,該方法不能被重寫或隱藏;用于類時,該類不能被繼承。

      接口的成員是不能使用該關鍵字的,道理和不能在abstract類使用final一樣。

      值得一提的是,如果使用final修飾類中的字段,那么該字段必須在構造函數中賦值,否則使用類實例調用的方式是無法對該字段進行賦值的,道理很簡單,類在實例化時,會為每一個成員字段賦初值,之后你如果再通過實例方式調用該final字段,就屬于二次賦值的情況了,這種情況是不允許的。在構造函數中為final變量賦值的方法叫做“延時賦值”(Java),相應的final變量叫做“空白final”(Java)。

      Final并不是一個C#中的關鍵字,但經常在C#面試題中出現,例如說說“Final、Finally、finalize的區別”,其實這已經超出C#的范疇,這三個關鍵字分別考核了J#、.Net 容錯方法、.Net垃圾收集機制,奇怪的是,每次我面試C#程序人員時,大部分人員對Final這個關鍵字并無陌生之感,相反卻答得頭頭是道,看來來面試之前,早在網上有所預習,呵呵。

      例子(摘自MSDN,已做翻譯):

 
public class Value
{

public int i = 1;
}

public class FinalData
{

//可認為等同于編譯時常量
final int i1 = 9;
static final int i2 = 99;
//public 常量:
public static final int i3 = 999;
//不可作為編譯時常量:
final int i4 = (int)(Math.random() * 11);
static final int i5 = (int)(Math.random() * 11);
Value v1
= new Value();
final Value v2
= new Value();
static final Value v3 = new Value();
// 數組:
final int[] a = { 1, 2, 3, 4, 5, 6 };
public void print(String id)
{
System.
out.println(id + ": " + "i4 = " + i4 + ", i5 = " + i5);
}

public static void main(String[] args)
{
FinalData fd1
= new FinalData();
// Error: Can't change value! (i1被描述為fianl的)
// fd1.i1++;
// OK. Object isn't constant(雖然v2是fianl的,但其中的變量并不受此約束)
fd1.v2.i++;
// OK. Not final.
fd1.v1 = new Value();
for (int i = 0; i < fd1.a.length; i++)
{
fd1.a[i]
++; // OK. Object isn't constant.(與上面那個v2一樣,數組是final的,但數組元素不受約束)
}
// Error: Can't change handle! (v2是final的)
// fd1.v2 = new Value();
// Error: Can't change handle! (v3是static final的,等同于常量)
// fd1.v3 = new Value();
// Error: Can't change handle!(數組本身是final,不可new)
// fd1.a = new int[3];
fd1.print("fd1");
System.
out.println("Creating new FinalData");
FinalData fd2
= new FinalData();
fd1.print(
"fd1");
fd2.print(
"fd2");
}
}

  答案:

  fd1: i4 = 0, i5 = 7

  Creating new FinalData

  fd1: i4 = 0, i5 = 7

  fd2: i4 = 8, i5 = 7

  總結:final是J#中的一種修飾符,在VS2008及以后版本中就放棄J#了,它與sealed不同的是fianl可以修飾變量,而sealed則不能,不過你可以通過readonly關鍵字來實現。

      關于二者對性能的提升作用,我認為有待考證,從理論層面來講,為難以證明的性能因素而特意使用此關鍵字有些得不償失。

4
0
 
 
 

文章列表

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

    IT工程師數位筆記本

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