c#靜態建構函式並跟golang比較

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

ORM架構在基類中定義各增刪改查方法,子類實現儲存儘可能簡潔,除了各屬性工作表示表結構(也就是POCO Plain Old Csharp Object)至少還得有表名等資訊。

考慮一下程式碼片段:

public abstract class RecordBase<T> where T : RecordBase<T>, new(){    protected static string TableName;    public static List<T> GetByID(int id)    {        using (var conn = OpenConnection())        {            var sql = "select * from " + TableName + " where ID = @id";            return conn.Query<T>(sql, new {id = id}).First();        }    }}public class User : RecordBase<User>{    public int Id { get; set; }    public string Name { get; set; }    static User ()    {        TableName = "Users";    }}

調用代碼則類似:

var u = User.GetByID(1);

User子類中的靜態建構函式中對TableName進行賦值;那麼父類中的GetByID方法執行時,便可以獲得正確的表名。

類型的靜態建構函式一般用於類型的待用資料初識化,它會並且僅會執行一次。

(RecordBase<T>若有多個子類,引用的TableName屬性相互獨立。)

看上去這個情境正式靜態建構函式的典型應用。

遺憾的是,上面代碼是有坑的。

MSDN中對靜態建構函式static constructor定義如下:

A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed once only. It is called automatically before the first instance is created or any static members are referenced.

GetByID方法是在父類RecordBase<T>中定義的;即便調用時是寫User.GetByID(),實際上是被引用的還是父類的靜態成員,而不是子類;如果程式之前沒有建立過任何子類執行個體,那子類的靜態建構函式到這裡仍可能未被執行。

也就是說如果程式的其它地方都沒有引用到User子類,那麼User.GetByID()會拋異常:TableName是空的。

必須先在調用GetByID之前,先建立一個User執行個體(或者調用User而不是父類的靜態成員),才能正常工作,比方說:

var tmp = new User();var u = User.GetByID(1);

如果ORM這麼實現,這就是一個大坑;如果強製程序初始化的時候,寫顯式代碼去調用一下各個子類,也是相當麻煩。

偶找到的解決方案是:添加父類的靜態建構函式,在其中觸發子類的靜態建構函式。

    public abstract class RecordBase<T> where T : RecordBase<T>, new()    {        protected static string TableName;        static RecordBase()        {            System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);        }    }

GetByID是父類的靜態成員,它被調用的話,父類的靜態建構函式便會先被運行時調用,而函數內通過範型有子類的類型,便可通過CompilerServices調用子類的靜態建構函式。

(RunClassConstructor函數即便被多次調用,類型的靜態建構函式也只會被運行一次。)

c#中的靜態建構函式雖然可以做到精準的控制各類型的初始化,需要的時候才調用,不重複調用。

但若是控製得不精準,或者說實現代碼時不充分注意,便可能在程式中留下坑。若缺乏相關經驗知識,那踩中坑時也會困惑不知如何處理。

實際上,我是當年曾經搞過相關的開發,所以這次才特意去測試了不執行個體化子類,直接調用GetByID以確定這塊是否存在問題。如果我是新手並且對文檔理解不夠細,那采坑是妥妥的。

c#這門語言在很多方面都相當優秀,但具體到類型、模組初始化這塊,做為Go程式員,我會說Go做得更好。

Go語言不允許無效引用,更不允許模組循環參考,確保了所有的Go語言模組/類型有清晰的依賴關係。

而所有模組被引用前,便自動運行模組init方法。

Go程式員需要考慮的情境簡單,自然也就不容易采坑。

有些語言或者說工具的“強大”是體現在功能繁多上,功能越多,組合情境就越多,程式員便需要去精心琢磨各個具體情境需要如何處理,這實際上是相當大的心智負擔,並且程式往往也只是做到“沒有明顯bug”。

而像Go這樣的語言,它的“強大”之處是體現於提供儘可能相互不重疊的少數功能,程式員可以輕易的考量所有潛在情境,寫“明顯沒有bug”的程式。

這事用術語來說是Orthogonality,偶當年第一次看到這英文詞是不懂,查中文翻譯:正交性。字都認識,但組成詞,依舊是不懂。

:)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.