久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

探索c#之不可變數(shù)據(jù)類型

 深秋微涼3 2016-02-07



不可變對象


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


var str='mushroomsir';

str.Substring(0, 6)


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


var age=18;


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


age=2;


此時(shí)會(huì)在棧中開辟新值2賦值給age變量,而不能改變18這個(gè)內(nèi)存里的值,,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 = '北大';


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


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


mutable.Name == '大毛';

mutable.Address == '清華';


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


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í)只能通過Contact2的構(gòu)造函數(shù)來初始化Name和Address字段。Contact2此時(shí)即為不可變對象,,因?yàn)閷ο蟊旧硎莻€(gè)不可變整體,。通過使用不可變對象可以不用擔(dān)心數(shù)據(jù)完整性,也能保證數(shù)據(jù)安全性,,不會(huì)被其他線程修改,。


自定義不可變集合


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


public interface IStack : IEnumerable

{

IStack Push(T value);

IStack Pop();

T Peek();

bool IsEmpty { get; }

}

public sealed class Stack : IStack

{

private sealed class EmptyStack : IStack

{

public bool IsEmpty { get { return true; } }

public T Peek() { throw new Exception('Empty stack'); }

public IStack Push(T value) { return new Stack(value, this); }

public IStack Pop() { throw new Exception('Empty stack'); }

public IEnumerator GetEnumerator() { yield break; }

IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }

}

private static readonly EmptyStack empty = new EmptyStack();

public static IStack Empty { get { return empty; } }

private readonly T head;

private readonly IStack tail;

private Stack(T head, IStack tail)

{

this.head = head;

this.tail = tail;

}

public bool IsEmpty { get { return false; } }

public T Peek() { return head; }

public IStack Pop() { return tail; }

public IStack Push(T value) { return new Stack(value, this); }

public IEnumerator GetEnumerator()

{

for (IStack stack = this; !stack.IsEmpty; stack = stack.Pop())

yield return stack.Peek();

}

IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }

}


  • 入棧時(shí)會(huì)實(shí)例化一個(gè)新棧對象

  • 將新值通過構(gòu)造函數(shù)傳入,,并存放在新對象Head位置,,舊棧對象放在在Tail位置引用

  • 出棧時(shí)返回當(dāng)前棧對象的Tail引用的棧對象


使用方法如下:


IStack s1 = Stack.Empty;

IStack s2 = s1.Push(10);

IStack s3 = s2.Push(20);

IStack s4 = s3.Push(30);

IStack v3 = s4.Pop();

foreach (var item in s4)

{

//dosomething

}


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


Net提供的不可變集合


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


Install-Package Microsoft.Bcl.Immutable


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


ImmutableStack a1 = ImmutableStack.Empty;

ImmutableStack a2 = a1.Push(10);

ImmutableStack a3 = a2.Push(20);

ImmutableStack a4 = a3.Push(30);

ImmutableStack iv3 = a4.Pop();


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


ImmutableStack a1 = ImmutableStack.Empty;

a1.Push(10); //不正確,a1仍是空值值,,push會(huì)生成新的棧,。

a1 = a1.Push(10); //需要將新棧重新賦值給a1


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


  • ImmutableStack

  • ImmutableQueue

  • ImmutableList

  • ImmutableHashSet

  • ImmutableSortedSet

  • ImmutableDictionary

  • ImmutableSortedDictionary


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




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


  • 集合共享安全,從不被改變

  • 訪問集合時(shí),,不需要鎖集合(線程安全)

  • 修改集合不擔(dān)心舊集合被改變

  • 書寫更簡潔,,函數(shù)式風(fēng)格。 var list = ImmutableList.Empty.Add(10).Add(20).Add(30);

  • 保證數(shù)據(jù)完整性,,安全性


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


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


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


ImmutableList immutable = ImmutableList.Empty;

//轉(zhuǎn)換成可批量操作的集合

var immutable2 = immutable.ToBuilder();

immutable2.Add('xx');

immutable2.Add('xxx');

//還原成不可變集合

immutable = immutable2.ToImmutable();


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




比較代碼如下:


private static void List()

{

var list = new List();

var sp = Stopwatch.StartNew();


for (int i = 0; i < 1000="" *="" 10000;="">

{

var obj = new object();

list.Add(obj);

}

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

}

private static void BuilderImmutableList()

{

var list = ImmutableList.Empty;

var sp = Stopwatch.StartNew();

var blist= list.ToBuilder();

for (int i = 0; i < 1000="" *="" 10000;="">

{

var obj = new object();

blist.Add(obj);

}

list=blist.ToImmutable();


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

}

private static void ImmutableList()

{

var list = ImmutableList.Empty;

var sp = Stopwatch.StartNew();


for (int i = 0; i < 1000="" *="" 10000;="">

{

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ì)長時(shí)間存儲(chǔ)在共享域內(nèi)存中,,任何能拿到dump文件的人都可以看到明文,增加了密碼被竊取的風(fēng)險(xiǎn),。當(dāng)然這不是一個(gè)新問題,,net2.0提供的有SecureString來進(jìn)行安全存儲(chǔ),使用時(shí)進(jìn)行恢復(fù)及清理,。


IntPtr addr = Marshal.SecureStringToBSTR(secureString);

string temp = Marshal.PtrToStringBSTR(addr);

Marshal.ZeroFreeBSTR(addr);

WriteProcessMemory(...)


    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多