成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

實(shí)體類(lèi)的動(dòng)態(tài)生成(一)

crossea / 2768人閱讀

摘要:前言在應(yīng)用開(kāi)發(fā)中,通常都會(huì)涉及各種實(shí)體類(lèi)的編寫(xiě),有時(shí)這些實(shí)體類(lèi)還需要實(shí)現(xiàn)接口以支持屬性變更通知,一般我們都會(huì)手寫(xiě)這些代碼或者通過(guò)工具根據(jù)數(shù)據(jù)庫(kù)表定義抑或別的什么模板映射文件之類(lèi)的來(lái)生成它們。

前言

在應(yīng)用開(kāi)發(fā)中,通常都會(huì)涉及各種 POJO/POCO 實(shí)體類(lèi)(DO, DTO, BO, VO)的編寫(xiě),有時(shí)這些實(shí)體類(lèi)還需要實(shí)現(xiàn) INotifyPropertyChanged 接口以支持屬性變更通知,一般我們都會(huì)手寫(xiě)這些代碼或者通過(guò)工具根據(jù)數(shù)據(jù)庫(kù)表定義抑或別的什么模板、映射文件之類(lèi)的來(lái)生成它們。

但是,在業(yè)務(wù)實(shí)現(xiàn)中往往伴隨著諸如“如何簡(jiǎn)單且高效的獲取某個(gè)實(shí)體實(shí)例有哪些屬性發(fā)生過(guò)變更?”、“變更后的值是什么?”這樣的問(wèn)題,而大致的解決方法有:

由實(shí)體容器來(lái)跟蹤實(shí)例的屬性變更;

改造實(shí)體類(lèi)(譬如繼承特定實(shí)體基類(lèi),在基類(lèi)中實(shí)現(xiàn)這些基礎(chǔ)構(gòu)造)。

方法(1)需要配合一整套架構(gòu)設(shè)計(jì)來(lái)提供支撐,也不是專(zhuān)為解決上述實(shí)體類(lèi)的問(wèn)題而設(shè),并且實(shí)現(xiàn)和使用也都不夠簡(jiǎn)單高效,故此略過(guò)不表。接下來(lái)我將通過(guò)幾篇文章來(lái)詳細(xì)闡述這些問(wèn)題的來(lái)由以及解決方案,并給出完整的代碼實(shí)現(xiàn)以及性能比對(duì)測(cè)試。

關(guān)于源碼

下面將要介紹的所有代碼均位于我們的開(kāi)源系列項(xiàng)目(地址:https://github.com/Zongsoft),項(xiàng)目主要采用 LGPL 2.1授權(quán)協(xié)議,歡迎大家參與并使用(請(qǐng)遵照授權(quán)協(xié)議)。而本文相關(guān)的源碼位于其中 Zongsoft.CoreLibrary 項(xiàng)目的 feature-data 分支(https://github.com/Zongsoft/Zongsoft.CoreLibrary/tree/feature-data)及其中的 /samples/Zongsoft.Samples.Entities 范例項(xiàng)目,由于目前我正在忙著造 Zongsoft.Data 數(shù)據(jù)引擎這個(gè)輪子,不排除后面介紹到的代碼會(huì)有一些調(diào)整,待該項(xiàng)目完成后這些代碼亦會(huì)合并到 master 分支中,敬請(qǐng)留意。

基礎(chǔ)版本

萬(wàn)里長(zhǎng)城也是從第一塊磚頭開(kāi)始磊起來(lái)的,就讓我們來(lái)搬第一塊磚吧:

public class User
{
    private uint _userId;
    private string _name;

    // 傳統(tǒng)寫(xiě)法
    public uint UserId
    {
        get {
            return _userId;
        }
        set {
            _userId = value;
        }
    }

    // C# 7.0 語(yǔ)法
    public string Name
    {
        get => _name;
        set => _name = value;
    }

    // 懶漢寫(xiě)法:僅限不需要操作成員字段的場(chǎng)景
    public string Namespace
    {
        get;
        set;
    }
}

以上代碼特地用了三種編碼方式,它們被C#編譯器生成的IL沒(méi)有模式上的不同,故而性能沒(méi)有任何區(qū)別,大家根據(jù)自己的口味采用某種即可,因?yàn)槲覀兊脑创a由于歷史原因可能會(huì)有一些混寫(xiě),在此一并做個(gè)展示而已。

由于業(yè)務(wù)需要,我們希望實(shí)體類(lèi)能支持屬性變更通知,即讓它支持 INotifyPropertyChanged 接口,這么簡(jiǎn)單的需求當(dāng)然不在話下:

public class User : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private uint _userId;
    private string _name;

    public uint UserId
    {
        get => _userId;
        set {
            if(_userId == value)
                return;

            _userId = value;
            this.OnPropertyChanged("UserId"); // 傳統(tǒng)寫(xiě)法
        }
    }

    public string Name
    {
        get => _name;
        set {
            if(_name == value)
                return;

            _name = value;
            this.OnPropertyChanged(nameof(Name)); // nameof 為 C# 7.0 新增操作符
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        // 注意 ?. 為 C# 7.0 新增操作符
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

一切看起來(lái)是那么完美,但是,當(dāng)我們寫(xiě)了幾個(gè)這樣的實(shí)體類(lèi),尤其是有些實(shí)體類(lèi)的屬性還不少時(shí),體驗(yàn)就有點(diǎn)糟糕了。自然我們會(huì)想到寫(xiě)個(gè)實(shí)體基類(lèi)來(lái)實(shí)現(xiàn)屬性變更通知的基礎(chǔ)構(gòu)造,當(dāng)然,在某些特定場(chǎng)景也可以通過(guò)工具來(lái)生成類(lèi)似上面這樣的C#實(shí)體類(lèi)文件,但工具生成的方式有一定局限性并且不易維護(hù)(譬如需要在生成的代碼基礎(chǔ)上進(jìn)行特定改造),在此不再贅述。

實(shí)體基類(lèi)

在進(jìn)行基礎(chǔ)類(lèi)庫(kù)或API設(shè)計(jì)的時(shí)候,我有個(gè)建議:__從應(yīng)用場(chǎng)景開(kāi)始__。具體的作法是,先嘗試編寫(xiě)使用這些API的應(yīng)用代碼,待各種應(yīng)用場(chǎng)景的使用代碼基本都完成后,API接口也就自然而然的確定了。譬如,在我們這個(gè)需求中我希望這么去使用實(shí)體基類(lèi):

public class User : ModelBase
{
    private uint _userId;
    private string _name;

    public uint UserId
    {
        get => _userId;
        set => this.SetPropertyValue(nameof(UserId), ref _userId, value);
    }

    public string Name
    {
        get => _name;
        set => this.SetPropertyValue(nameof(Name), ref _name, value);
    }
}

有了這樣的實(shí)體基類(lèi)后,增強(qiáng)了功能后代碼依然如第一塊磚的“基礎(chǔ)版本”一樣簡(jiǎn)潔,真是高興啊!但這就夠了么,能不能把具體實(shí)體類(lèi)里面的成員字段也省了,交給基類(lèi)來(lái)處理呢?嗯,有點(diǎn)意思,試著寫(xiě)下應(yīng)用場(chǎng)景代碼:

public class User : ModelBase
{
    public uint UserId
    {
        get => (uint)this.GetPropertyValue(nameof(UserId));
        set => this.SetPropertyValue(nameof(UserId), value);
    }
}

看起來(lái)棒極了,代碼變得更簡(jiǎn)潔了,真是天才?。〉?,喪心病狂的 C# 設(shè)計(jì)者似乎看到了這種普遍的需求,于是在 C# 5 中增加了 System.Runtime.CompilerServices.CallerMemberNameAttribute 自定義標(biāo)記,C# 編譯器將自動(dòng)把調(diào)用者名字生成出來(lái)傳遞給加注了該標(biāo)記的參數(shù),因此這樣的代碼還可以繼續(xù)簡(jiǎn)化:

public class User : ModelBase
{
    public uint UserId
    {
        get => (uint)this.GetPropertyValue();
        set => this.SetPropertyValue(value);
    }
}

但是,屬性的 getter 里面的那個(gè)類(lèi)型強(qiáng)制轉(zhuǎn)換,怎么看都像是一朵“烏云”啊,能不能把它也去掉呢?嗯,利用C#的泛型類(lèi)型推斷可以完美解決它,繼續(xù)強(qiáng)勢(shì)進(jìn)化:

public class User : ModelBase
{
    public uint UserId
    {
        get => this.GetPropertyValue(() => this.UserId);
        set => this.SetPropertyValue(() => this.UserId, value);
    }
}

哇喔,有點(diǎn)小崇拜自己了,這代碼漂亮的一批!至此,實(shí)體基類(lèi)的API接口基本確定,已經(jīng)迫不及待想要去實(shí)現(xiàn)它了。

提示:由于采用 CallerMemberNameAttribute 自定義標(biāo)記的參數(shù)會(huì)導(dǎo)致 C# 編譯器要求該參數(shù)必需有默認(rèn)值,因此有些 SetPropertyValue(...) 方法重載版本中 propertyName 參數(shù)需要位于參數(shù)集的最后,為了與上面的范例代碼對(duì)應(yīng)就省略了這些參數(shù)的標(biāo)記,并保持與原有范例相同的簽名設(shè)計(jì)。

using System;
using System.Linq.Expressions;

public class ModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected object GetPropertyValue([CallerMemberName]string propertyName = null);
    protected T GetPropertyValue(Expression> property);

    protected void SetPropertyValue(string propertyName, ref T field, T value);
    protected void SetPropertyValue(string propertyName, T value);
    protected void SetPropertyValue(Expression> property, T value);
}

實(shí)體基類(lèi)的實(shí)現(xiàn)主要思路就是采用字典來(lái)記錄各屬性的變更值,有了這個(gè)基礎(chǔ),要繼續(xù)增加諸如“獲取哪些屬性發(fā)生過(guò)變更”之類(lèi)的需求自然就很容易了:

public class ModelBase : INotifyPropertyChanged
{
    // other members

    public bool HasChanges(params string[] propertyNames);
    public IDictionary GetChangedPropertys();
}

具體的代碼就不在這里貼出了,有興趣的可以參考:https://github.com/Zongsoft/Zongsoft.CoreLibrary/blob/master/src/Common/ModelBase.cs,從功能角度上看,目前的設(shè)計(jì)還是不錯(cuò)的。但是,某些方法的設(shè)計(jì)有嚴(yán)重性能缺陷的,主要有以下幾點(diǎn):

每次讀寫(xiě)屬性都會(huì)解析Lambda 表達(dá)式的操作會(huì)產(chǎn)生巨大的性能損耗;

采用字典來(lái)保存實(shí)體屬性值的設(shè)計(jì)機(jī)制,會(huì)導(dǎo)致值類(lèi)型的屬性讀寫(xiě)反復(fù)被裝箱(Boxing)、拆箱(Unboxing);

字典的讀寫(xiě)效率也遠(yuǎn)低于直接操作成員字段的語(yǔ)言原語(yǔ)方式。

綜上所述,雖然目前方案有性能缺陷,但應(yīng)對(duì)一般場(chǎng)景其實(shí)是沒(méi)有問(wèn)題的,而且功能和易用性方面都是很好的;但是,性能對(duì)于后臺(tái)程序猿而言猶如懸在頭頂?shù)?達(dá)摩克利斯之劍,這正是這個(gè)系列文章要最終解決的問(wèn)題。在此之前,如果大家有關(guān)于這個(gè)問(wèn)題的性能優(yōu)化方案,歡迎關(guān)注我們的公眾號(hào)(Zongsoft)留言討論。

敬請(qǐng)期待更精彩的下篇,關(guān)注我們的公眾號(hào)可以第一時(shí)間看到哦!

提示

本文可能會(huì)更新,請(qǐng)閱讀原文: https://zongsoft.github.io/blog/zh-cn/zongsoft/entity-dynamic-generation-1,以避免因內(nèi)容陳舊而導(dǎo)致的謬誤,同時(shí)亦有更好的閱讀體驗(yàn)。

本作品采用?知識(shí)共享署名-非商業(yè)性使用-相同方式共享 4.0 國(guó)際許可協(xié)議?進(jìn)行許可。歡迎轉(zhuǎn)載、使用、重新發(fā)布,但必須保留本文的署名 鐘峰(包含鏈接:http://zongsoft.github.io),不得用于商業(yè)目的,基于本文修改后的作品務(wù)必以相同的許可發(fā)布。如有任何疑問(wèn)或授權(quán)方面的協(xié)商,請(qǐng)致信給我 ([email protected])。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76656.html

相關(guān)文章

  • 實(shí)體類(lèi)的動(dòng)態(tài)生成(二)

    摘要:源碼位于項(xiàng)目的命名空間中表示數(shù)據(jù)實(shí)體的接口。獲取實(shí)體中發(fā)生過(guò)變更的屬性集。新辦法解決辦法其實(shí)很簡(jiǎn)單,正是本文的標(biāo)題動(dòng)態(tài)生成,徹底解放實(shí)現(xiàn)者并確保實(shí)現(xiàn)的正確性。業(yè)務(wù)方不再定義具體的實(shí)體類(lèi),而是定義實(shí)體接口即可,實(shí)體類(lèi)將由實(shí)體生成器來(lái)動(dòng)態(tài)生成。 前言 由于采用字典的方式來(lái)保存屬性變更值的底層設(shè)計(jì)思想,導(dǎo)致了性能問(wèn)題,雖然.NET的字典實(shí)現(xiàn)已經(jīng)很高效了,但相對(duì)于直接讀寫(xiě)字段的方式而言依然有巨...

    mochixuan 評(píng)論0 收藏0
  • Java動(dòng)態(tài)代理 jdk和cglib的實(shí)現(xiàn)比較

    摘要:與靜態(tài)代理對(duì)比,動(dòng)態(tài)代理是在動(dòng)態(tài)生成代理類(lèi),由代理類(lèi)完成對(duì)具體方法的封裝,實(shí)現(xiàn)的功能。本文將分析中兩種動(dòng)態(tài)代理的實(shí)現(xiàn)方式,和,比較它們的異同。那如何動(dòng)態(tài)編譯呢你可以使用,這是一個(gè)封裝了的庫(kù),幫助你方便地實(shí)現(xiàn)動(dòng)態(tài)編譯源代碼。 發(fā)現(xiàn)Java面試很喜歡問(wèn)Spring AOP怎么實(shí)現(xiàn)的之類(lèi)的問(wèn)題,所以寫(xiě)一篇文章來(lái)整理一下。關(guān)于AOP和代理模式的概念這里并不做贅述,而是直奔主題,即AOP的實(shí)現(xiàn)方...

    h9911 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<