程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中的弗成變數據類型引見(弗成變對象、弗成變聚集)

C#中的弗成變數據類型引見(弗成變對象、弗成變聚集)

編輯:C#入門知識

C#中的弗成變數據類型引見(弗成變對象、弗成變聚集)。本站提示廣大學習愛好者:(C#中的弗成變數據類型引見(弗成變對象、弗成變聚集))文章只能為提供參考,不一定能成為您想要的結果。以下是C#中的弗成變數據類型引見(弗成變對象、弗成變聚集)正文


弗成變對象

弗成變(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(...)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved