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

來源:互聯網
上載者:User

    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中無法重寫任何方法)

    ●描述屬性:

    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 系列之六--深入淺出.NetJust-In-Time 編譯機制(下),這也不符合程式局部性原理。http://www.cnblogs.com/isline/archive/2009/12/27/1633453.html),而對於sealed類內部方法的內聯,很大原因也是由於方法槽映射關係決定的,sealed作用有待考證。

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

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

    ● Sealed不能同時abstract?

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

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

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

    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關鍵字來實現。

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.