亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#中的不可變數據類型介紹(不可變對象、不可變集合)

2020-01-24 02:01:03
字體:
來源:轉載
供稿:網友

不可變對象

不可變(immutable): 即對象一旦被創建初始化后,它們的值就不能被改變,之后的每次改變都會產生一個新對象。

復制代碼 代碼如下:

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

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

var age=18;

當存儲值18的內存分配給age變量時,它的內存值也是不可以被修改的。
復制代碼 代碼如下:

age=2;

此時會在棧中開辟新值2賦值給age變量,而不能改變18這個內存里的值,int在c#中也是不可變的。
復制代碼 代碼如下:

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 = "北大";

我們實例化MutableContact賦值給mutable,隨后我們可以修改MutableContact對象內部字段值,它已經不是初始后的值,可稱為可變(mutable)對象。

可變對象在多線程并發中共享,是存在一些問題的。多線程下A線程賦值到 Name = "大毛" 這一步,其他的線程有可能讀取到的數據就是:

復制代碼 代碼如下:

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

很明顯這樣數據完整性就不能保障,也有稱數據撕裂。我們把可變對象更改為不可變對象如下:
復制代碼 代碼如下:

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);
    }
}

使用時只能通過Contact2的構造函數來初始化Name和Address字段。Contact2此時即為不可變對象,因為對象本身是個不可變整體。通過使用不可變對象可以不用擔心數據完整性,也能保證數據安全性,不會被其他線程修改。

自定義不可變集合

我們去枚舉可變集合時,出于線程安全的考慮我們往往需要進行加鎖處理,防止該集合在其他線程被修改,而使用不可變集合則能避免這個問題。我們平常使用的數據結構都是采用可變模式來實現的,那怎么實現一個不可變數據結構呢!以棧來示例,具體代碼如下:

復制代碼 代碼如下:

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.入棧時會實例化一個新棧對象
2.將新值通過構造函數傳入,并存放在新對象Head位置,舊棧對象放在在Tail位置引用
3.出棧時返回當前棧對象的Tail引用的棧對象

使用方法如下:

復制代碼 代碼如下:

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都是一個新對象,舊對象不可修改,這樣在枚舉集合就不需要擔心其他線程修改了。

Net提供的不可變集合

不可變隊列,不可變列表等數據結構如果都自己實現工作量確實有點大。幸好的是Net在4.5版本已經提供了不可變集合的基礎類庫。 使用Nuget安裝:

復制代碼 代碼如下:

Install-Package Microsoft.Bcl.Immutable

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

 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不可變列表集合有一點要注意的是,當我們Push值時要重新賦值給原變量才正確,因為push后會生成一個新對象,原a1只是舊值:

復制代碼 代碼如下:

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

NET提供的常用數據結構

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

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

不可變優點

1.集合共享安全,從不被改變
2.訪問集合時,不需要鎖集合(線程安全)
3.修改集合不擔心舊集合被改變
4.書寫更簡潔,函數式風格。 var list = ImmutableList.Empty.Add(10).Add(20).Add(30);
5.保證數據完整性,安全性

不可變對象缺點

不可變本身的優點即是缺點,當每次對象/集合操作都會返回個新值。而舊值依舊會保留一段時間,這會使內存有極大開銷,也會給GC造成回收負擔,性能也比可變集合差的多。

跟string和StringBuild一樣,Net提供的不可變集合也增加了批量操作的API,用來避免大量創建對象:

復制代碼 代碼如下:

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

我們來對比下可變集合、不可變Builder集合、不可變集合的性能,添加新對象1000W次:

比較代碼如下:

復制代碼 代碼如下:

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);
        }

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

復制代碼 代碼如下:

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文国产成人精品| 中文字幕日韩专区| 精品国产网站地址| 欧美性xxxx极品hd满灌| 亚洲性xxxx| 国产成人激情视频| 欧美怡红院视频一区二区三区| 欧美性做爰毛片| 国产精品白丝av嫩草影院| 欧美激情三级免费| 亚洲免费福利视频| 日韩不卡中文字幕| 国内精品一区二区三区| 粉嫩av一区二区三区免费野| 国产精品av网站| 黄色成人av在线| 国产精品视频资源| 浅井舞香一区二区| 性欧美长视频免费观看不卡| 国产日韩精品综合网站| 精品国产乱码久久久久久婷婷| 日韩精品免费在线播放| 欧美激情在线狂野欧美精品| 久久免费高清视频| 九九精品在线观看| 亚洲毛片在线观看| 国内精品久久久久久影视8| 国产黑人绿帽在线第一区| 色综合久久天天综线观看| 国产精品久久久久久亚洲调教| 国产在线视频91| 国产精品视频久| 欧美理论电影在线播放| 欧洲一区二区视频| 国产精品欧美日韩| 久久成人免费视频| 亚洲无亚洲人成网站77777| 久久在精品线影院精品国产| 欧美亚洲在线视频| 在线观看精品自拍私拍| 亚洲影院污污.| 国产精品第一页在线| 中文字幕av一区二区| 亚洲成人av在线| 国产美女高潮久久白浆| 欧美激情免费视频| 136fldh精品导航福利| 国产精品视频99| 欧美一级bbbbb性bbbb喷潮片| 欧美乱妇40p| 成人免费淫片aa视频免费| 久久亚洲私人国产精品va| 日本精品一区二区三区在线播放视频| 国产精品自拍偷拍视频| 久久久成人精品| 国产亚洲精品91在线| 亚洲欧美日本精品| 欧美洲成人男女午夜视频| 成人h视频在线观看播放| 欧洲美女7788成人免费视频| 国产丝袜精品第一页| 欧美在线视频在线播放完整版免费观看| 久久成人精品一区二区三区| 欧美日韩中文字幕在线| 日韩成人在线播放| 久久综合久久美利坚合众国| 亚洲午夜女主播在线直播| 国产亚洲xxx| 欧美黄色三级网站| 亚洲欧美日本另类| 国产精品永久免费视频| 色综合久久精品亚洲国产| 在线成人激情黄色| 欧美电影免费观看高清| 国产精品极品尤物在线观看| 成人精品在线视频| 欧美一区在线直播| 欧美巨猛xxxx猛交黑人97人| 欧美亚洲国产视频| 91高清免费在线观看| 俺去亚洲欧洲欧美日韩| 欧美老少做受xxxx高潮| 久久人人97超碰精品888| 国内精品小视频| 国产精品自产拍在线观看中文| 成人网页在线免费观看| 国产日韩精品在线播放| 国产精品国产三级国产aⅴ浪潮| 国产91色在线| 国产精品一区二区三区在线播放| 亚洲欧美日韩中文在线制服| 亚洲人成在线观看网站高清| 日韩美女在线播放| 69久久夜色精品国产69| 精品色蜜蜜精品视频在线观看| 亚洲一区二区三区视频| 日本sm极度另类视频| 国产亚洲欧美一区| 亚洲va男人天堂| 欧美成人剧情片在线观看| 国产成人啪精品视频免费网| 亚洲一级一级97网| 成人黄色片在线| 成人免费高清完整版在线观看| 国产精品青草久久久久福利99| 欧美二区在线播放| 韩剧1988免费观看全集| 欧美激情精品在线| 日韩性生活视频| 国产成人小视频在线观看| 亚洲视频日韩精品| 国产精品久久久久久一区二区| 性色av香蕉一区二区| 96sao精品视频在线观看| 久久久久久一区二区三区| 欧美精品在线看| 国产精品高潮呻吟视频| 少妇高潮 亚洲精品| 国内免费精品永久在线视频| 欧美亚洲国产视频小说| 日韩av电影中文字幕| 97在线观看视频国产| 91九色单男在线观看| 久久久久久久久久久久久久久久久久av| 久久久久久久久亚洲| 精品国偷自产在线视频99| 91国产在线精品| 日韩精品中文在线观看| 成人黄色av免费在线观看| 久久成人人人人精品欧| 欧美日本亚洲视频| 国产aⅴ夜夜欢一区二区三区| 2019亚洲男人天堂| xxav国产精品美女主播| 国产日韩精品在线| 国产精品jvid在线观看蜜臀| 北条麻妃一区二区三区中文字幕| 国产69精品久久久久9| 亚洲人午夜色婷婷| 亚洲最大福利视频网站| 久久人人爽人人爽人人片亚洲| 一区国产精品视频| 欧美性在线观看| 亚洲第一黄色网| 黑人巨大精品欧美一区免费视频| 国产一区香蕉久久| 欧美亚洲伦理www| 九九精品在线视频| 97热在线精品视频在线观看| 97超级碰在线看视频免费在线看| 久久精品成人欧美大片| 日韩大陆毛片av| 国产精品免费电影| 日韩av免费在线播放| 国产成人精品一区二区三区| 日韩av三级在线观看| 国产亚洲美女精品久久久| 欧美巨大黑人极品精男| 国产一区二区激情| 97在线观看视频| 国产一区二区av| 欧美日韩国内自拍| 亚洲第一免费网站| 欧美日韩亚洲激情|