最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#中的不可變數(shù)據(jù)類(lèi)型介紹(不可變對(duì)象、不可變集合)

 更新時(shí)間:2015年04月10日 09:04:24   投稿:junjie  
這篇文章主要介紹了C#中的不可變數(shù)據(jù)類(lèi)型介紹(不可變對(duì)象、不可變集合),本文講解了不可變對(duì)象、自定義不可變集合、Net提供的不可變集合、不可變優(yōu)點(diǎn)、不可變對(duì)象缺點(diǎn)等內(nèi)容,需要的朋友可以參考下

不可變對(duì)象

不可變(immutable): 即對(duì)象一旦被創(chuàng)建初始化后,它們的值就不能被改變,之后的每次改變都會(huì)產(chǎn)生一個(gè)新對(duì)象。

復(fù)制代碼 代碼如下:

var str="mushroomsir";
str.Substring(0, 6)

c#中的string是不可變的,Substring(0, 6)返回的是一個(gè)新字符串值,而原字符串在共享域中是不變的。另外一個(gè)StringBuilder是可變的,這也是推薦使用StringBuilder的原因。
復(fù)制代碼 代碼如下:

var age=18;

當(dāng)存儲(chǔ)值18的內(nèi)存分配給age變量時(shí),它的內(nèi)存值也是不可以被修改的。
復(fù)制代碼 代碼如下:

age=2;

此時(shí)會(huì)在棧中開(kāi)辟新值2賦值給age變量,而不能改變18這個(gè)內(nèi)存里的值,int在c#中也是不可變的。
復(fù)制代碼 代碼如下:

class Contact
{
    public string Name { get;  set; }
    public string Address { get;  set; }
    public Contact(string contactName, string contactAddress)
    {
        Name = contactName;
        Address = contactAddress;              
    }
}
   var mutable = new Contact("二毛", "清華");
   mutable.Name = "大毛";
   mutable.Address = "北大";

我們實(shí)例化MutableContact賦值給mutable,隨后我們可以修改MutableContact對(duì)象內(nèi)部字段值,它已經(jīng)不是初始后的值,可稱(chēng)為可變(mutable)對(duì)象。

可變對(duì)象在多線程并發(fā)中共享,是存在一些問(wèn)題的。多線程下A線程賦值到 Name = "大毛" 這一步,其他的線程有可能讀取到的數(shù)據(jù)就是:

復(fù)制代碼 代碼如下:

  mutable.Name == "大毛";
  mutable.Address == "清華";

很明顯這樣數(shù)據(jù)完整性就不能保障,也有稱(chēng)數(shù)據(jù)撕裂。我們把可變對(duì)象更改為不可變對(duì)象如下:
復(fù)制代碼 代碼如下:

public class Contact2
{
    public string Name { get; private set; }
    public string Address { get; private set; }
    private Contact2(string contactName, string contactAddress)
    {
        Name = contactName;
        Address = contactAddress;              
    }
    public static Contact2 CreateContact(string name, string address)
    {
        return new Contact2(name, address);
    }
}

使用時(shí)只能通過(guò)Contact2的構(gòu)造函數(shù)來(lái)初始化Name和Address字段。Contact2此時(shí)即為不可變對(duì)象,因?yàn)閷?duì)象本身是個(gè)不可變整體。通過(guò)使用不可變對(duì)象可以不用擔(dān)心數(shù)據(jù)完整性,也能保證數(shù)據(jù)安全性,不會(huì)被其他線程修改。

自定義不可變集合

我們?nèi)ッ杜e可變集合時(shí),出于線程安全的考慮我們往往需要進(jìn)行加鎖處理,防止該集合在其他線程被修改,而使用不可變集合則能避免這個(gè)問(wèn)題。我們平常使用的數(shù)據(jù)結(jié)構(gòu)都是采用可變模式來(lái)實(shí)現(xiàn)的,那怎么實(shí)現(xiàn)一個(gè)不可變數(shù)據(jù)結(jié)構(gòu)呢!以棧來(lái)示例,具體代碼如下:

復(fù)制代碼 代碼如下:

public interface IStack<T> : IEnumerable<T>
{
    IStack<T> Push(T value);
    IStack<T> Pop();
    T Peek();
    bool IsEmpty { get; }
}
public sealed class Stack<T> : IStack<T>
{
    private sealed class EmptyStack : IStack<T>
    {
        public bool IsEmpty { get { return true; } }
        public T Peek() { throw new Exception("Empty stack"); }
        public IStack<T> Push(T value) { return new Stack<T>(value, this); }
        public IStack<T> Pop() { throw new Exception("Empty stack"); }
        public IEnumerator<T> GetEnumerator() { yield break; }
        IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
    }
    private static readonly EmptyStack empty = new EmptyStack();
    public static IStack<T> Empty { get { return empty; } }
    private readonly T head;
    private readonly IStack<T> tail;
    private Stack(T head, IStack<T> tail)
    {
        this.head = head;
        this.tail = tail;
    }
    public bool IsEmpty { get { return false; } }
    public T Peek() { return head; }
    public IStack<T> Pop() { return tail; }
    public IStack<T> Push(T value) { return new Stack<T>(value, this); }
    public IEnumerator<T> GetEnumerator()
    {
        for (IStack<T> stack = this; !stack.IsEmpty; stack = stack.Pop())
            yield return stack.Peek();
    }
    IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}

1.入棧時(shí)會(huì)實(shí)例化一個(gè)新棧對(duì)象
2.將新值通過(guò)構(gòu)造函數(shù)傳入,并存放在新對(duì)象Head位置,舊棧對(duì)象放在在Tail位置引用
3.出棧時(shí)返回當(dāng)前棧對(duì)象的Tail引用的棧對(duì)象

使用方法如下:

復(fù)制代碼 代碼如下:

IStack<int> s1 = Stack<int>.Empty;
IStack<int> s2 = s1.Push(10);
IStack<int> s3 = s2.Push(20);
IStack<int> s4 = s3.Push(30);
IStack<int> v3 = s4.Pop();
foreach (var item in s4)
{
//dosomething
}

每次Push都是一個(gè)新對(duì)象,舊對(duì)象不可修改,這樣在枚舉集合就不需要擔(dān)心其他線程修改了。

Net提供的不可變集合

不可變隊(duì)列,不可變列表等數(shù)據(jù)結(jié)構(gòu)如果都自己實(shí)現(xiàn)工作量確實(shí)有點(diǎn)大。幸好的是Net在4.5版本已經(jīng)提供了不可變集合的基礎(chǔ)類(lèi)庫(kù)。 使用Nuget安裝:

復(fù)制代碼 代碼如下:

Install-Package Microsoft.Bcl.Immutable

使用如下,和上面我們自定義的幾乎一樣:
復(fù)制代碼 代碼如下:

 ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
        ImmutableStack<int> a2 = a1.Push(10);
        ImmutableStack<int> a3 = a2.Push(20);
        ImmutableStack<int> a4 = a3.Push(30);
        ImmutableStack<int> iv3 = a4.Pop();

使用Net不可變列表集合有一點(diǎn)要注意的是,當(dāng)我們Push值時(shí)要重新賦值給原變量才正確,因?yàn)閜ush后會(huì)生成一個(gè)新對(duì)象,原a1只是舊值:

復(fù)制代碼 代碼如下:

   ImmutableStack<int> a1 = ImmutableStack<int>.Empty;
   a1.Push(10); //不正確,a1仍是空值值,push會(huì)生成新的棧。
   a1 = a1.Push(10); //需要將新棧重新賦值給a1

NET提供的常用數(shù)據(jù)結(jié)構(gòu)

1.ImmutableStack
2.ImmutableQueue
3.ImmutableList
4.ImmutableHashSet
5.ImmutableSortedSet
6.ImmutableDictionary<K, V>
7.ImmutableSortedDictionary<K, V>

不可變集合和可變集合在算法復(fù)雜度上的不同:

不可變優(yōu)點(diǎn)

1.集合共享安全,從不被改變
2.訪問(wèn)集合時(shí),不需要鎖集合(線程安全)
3.修改集合不擔(dān)心舊集合被改變
4.書(shū)寫(xiě)更簡(jiǎn)潔,函數(shù)式風(fēng)格。 var list = ImmutableList.Empty.Add(10).Add(20).Add(30);
5.保證數(shù)據(jù)完整性,安全性

不可變對(duì)象缺點(diǎn)

不可變本身的優(yōu)點(diǎn)即是缺點(diǎn),當(dāng)每次對(duì)象/集合操作都會(huì)返回個(gè)新值。而舊值依舊會(huì)保留一段時(shí)間,這會(huì)使內(nèi)存有極大開(kāi)銷(xiāo),也會(huì)給GC造成回收負(fù)擔(dān),性能也比可變集合差的多。

跟string和StringBuild一樣,Net提供的不可變集合也增加了批量操作的API,用來(lái)避免大量創(chuàng)建對(duì)象:

復(fù)制代碼 代碼如下:

ImmutableList<string> immutable = ImmutableList<string>.Empty;
        //轉(zhuǎn)換成可批量操作的集合
        var immutable2 = immutable.ToBuilder();
        immutable2.Add("xx");
        immutable2.Add("xxx");
        //還原成不可變集合
        immutable = immutable2.ToImmutable();

我們來(lái)對(duì)比下可變集合、不可變Builder集合、不可變集合的性能,添加新對(duì)象1000W次:

比較代碼如下:

復(fù)制代碼 代碼如下:

private static void List()
        {
            var list = new List<object>();
            var sp = Stopwatch.StartNew();

            for (int i = 0; i < 1000 * 10000; i++)
            {
                var obj = new object();
                list.Add(obj);
            }
            Console.WriteLine("可變列表集合:"+sp.Elapsed);
        }
     
        private static void BuilderImmutableList()
        {
            var list = ImmutableList<object>.Empty;
            var sp = Stopwatch.StartNew();
            var blist= list.ToBuilder();
            for (int i = 0; i < 1000 * 10000; i++)
            {
                var obj = new object();
                blist.Add(obj);
            }
            list=blist.ToImmutable();

            Console.WriteLine("不可變Builder列表集合:"+sp.Elapsed);
        }
        private static void ImmutableList()
        {
            var list = ImmutableList<object>.Empty;
            var sp = Stopwatch.StartNew();

            for (int i = 0; i < 1000 * 10000; i++)
            {
                var obj = new object();
                list = list.Add(obj);
            }

            Console.WriteLine("不可變列表集合:" + sp.Elapsed);
        }

另外一個(gè)缺點(diǎn)比較有趣,也有不少人忽略。 由于string的不可變特性,所以當(dāng)我們使用string在保存敏感信息時(shí),就需要特別注意。
比如密碼 var pwd="mushroomsir",此時(shí)密碼會(huì)以明文存儲(chǔ)在內(nèi)存中,也許你稍后會(huì)加密置空等,但這都是會(huì)生成新值的。而明文會(huì)長(zhǎng)時(shí)間存儲(chǔ)在共享域內(nèi)存中,任何能拿到dump文件的人都可以看到明文,增加了密碼被竊取的風(fēng)險(xiǎn)。當(dāng)然這不是一個(gè)新問(wèn)題,net2.0提供的有SecureString來(lái)進(jìn)行安全存儲(chǔ),使用時(shí)進(jìn)行恢復(fù)及清理。

復(fù)制代碼 代碼如下:

IntPtr addr = Marshal.SecureStringToBSTR(secureString);
string temp = Marshal.PtrToStringBSTR(addr);
Marshal.ZeroFreeBSTR(addr);
WriteProcessMemory(...)

相關(guān)文章

  • C#讀寫(xiě)Excel的流程步驟

    C#讀寫(xiě)Excel的流程步驟

    這篇文章主要介紹了詳解C#讀寫(xiě)Excel的流程步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起來(lái)學(xué)習(xí)吧
    2023-12-12
  • 一文詳解C#?Chart控件

    一文詳解C#?Chart控件

    這篇文章主要介紹了一文學(xué)習(xí)C#?Chart控件,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • 登錄驗(yàn)證全局控制的幾種方式總結(jié)(session)

    登錄驗(yàn)證全局控制的幾種方式總結(jié)(session)

    在登陸驗(yàn)證或者其他需要用到session全局變量的時(shí)候,歸結(jié)起來(lái),主要有以下三種較方便的實(shí)現(xiàn)方式。(其中個(gè)人較喜歡使用第一種實(shí)現(xiàn)方法)
    2014-01-01
  • 淺析C# web訪問(wèn)mysql數(shù)據(jù)庫(kù)-整理歸納總結(jié)

    淺析C# web訪問(wèn)mysql數(shù)據(jù)庫(kù)-整理歸納總結(jié)

    本篇文章是對(duì)C#中的web訪問(wèn)mysql數(shù)據(jù)庫(kù)的一些知識(shí)點(diǎn)進(jìn)行了整理歸納總結(jié),需要的朋友可以參考下
    2013-07-07
  • C#實(shí)現(xiàn)簡(jiǎn)單過(guò)濾非法字符實(shí)例

    C#實(shí)現(xiàn)簡(jiǎn)單過(guò)濾非法字符實(shí)例

    這篇文章主要介紹了C#實(shí)現(xiàn)簡(jiǎn)單過(guò)濾非法字符的方法,涉及C#針對(duì)字符串遍歷與判斷的相關(guān)技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下
    2015-11-11
  • WinForm特效之桌面上的遮罩層實(shí)現(xiàn)方法

    WinForm特效之桌面上的遮罩層實(shí)現(xiàn)方法

    這篇文章主要介紹了WinForm特效之桌面上的遮罩層實(shí)現(xiàn)方法,是一個(gè)非常實(shí)用的技巧,需要的朋友可以參考下
    2014-09-09
  • 淺析C# Dynamic關(guān)鍵字

    淺析C# Dynamic關(guān)鍵字

    這篇文章主要介紹了C# Dynamic關(guān)鍵字的相關(guān)資料,文中講解非常細(xì)致,對(duì)大家學(xué)習(xí)C# Dynamic關(guān)鍵字有所幫助,感興趣的朋友可以了解下
    2020-08-08
  • 簡(jiǎn)單實(shí)現(xiàn)C#窗體程序判斷是否閏年

    簡(jiǎn)單實(shí)現(xiàn)C#窗體程序判斷是否閏年

    這篇文章主要介紹了簡(jiǎn)單實(shí)現(xiàn)C#窗體程序判斷是否閏年的相關(guān)代碼,禁止窗體調(diào)整大小,關(guān)閉窗體前的判斷,感興趣的小伙伴們可以參考一下
    2016-07-07
  • c#調(diào)整圖片分辨率的實(shí)現(xiàn)示例

    c#調(diào)整圖片分辨率的實(shí)現(xiàn)示例

    本文主要介紹了c#調(diào)整圖片分辨率的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-10-10
  • WPF實(shí)現(xiàn)動(dòng)畫(huà)效果(一)之基本概念

    WPF實(shí)現(xiàn)動(dòng)畫(huà)效果(一)之基本概念

    這篇文章介紹了WPF實(shí)現(xiàn)動(dòng)畫(huà)效果之基本概念,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06

最新評(píng)論

横山县| 莲花县| 治县。| 天峨县| 旌德县| 新晃| 平乡县| 萨嘎县| 绵竹市| 扎兰屯市| 平度市| 元阳县| 大悟县| 淮阳县| 清河县| 洞口县| 腾冲县| 阿合奇县| 宜城市| 天镇县| 龙山县| 麻江县| 谷城县| 海林市| 尉犁县| 林芝县| 肥城市| 安平县| 虎林市| 会宁县| 峨眉山市| 东海县| 离岛区| 于都县| 镇远县| 抚松县| 沧源| 镇平县| 巴彦淖尔市| 临安市| 台州市|