C#基礎(chǔ)提升系列——C#特殊集合

C# 特殊集合

C#中的特殊集合主要有:

  • 不可變的集合
  • 并發(fā)的集合
  • 位數(shù)組合位矢量
  • 可觀察的集合

不變的集合

如果對(duì)象可以改變其狀態(tài),就很難在多個(gè)同時(shí)運(yùn)行的任務(wù)中使用。這些集合必須同步。如果對(duì)象不能改變其狀態(tài),就很容易在多個(gè)線程中使用。不能改變的對(duì)象稱為不變的對(duì)象;不能改變的集合稱為不變的集合。

為了使用不可變的集合,需要添加NuGget包System.Collections.Immutalbe,關(guān)于此命名空間下的詳細(xì)介紹,請(qǐng)點(diǎn)擊此處進(jìn)行查看,本文只對(duì)其進(jìn)行簡(jiǎn)單的示例說明。

ImmutableArray

該類提供創(chuàng)建不可變數(shù)組的方法。例如:

ImmutableArray<string> a1= ImmutableArray.Create<string>();

上述語句用于創(chuàng)建一個(gè)string類型的不可變數(shù)組,注意,上述雖然都是ImmutableArray,但是卻是兩種不同的類型:非泛型類ImmutableArray調(diào)用Create()靜態(tài)方法返回泛型ImmutableArray結(jié)構(gòu)。其中,Create方法被重載,這個(gè)方法的其他變體允許傳送任意數(shù)量的元素。

可以使用Add()方法添加新的元素,Add()方法不會(huì)改變不變集合本身,而是返回一個(gè)新的不變集合。

ImmutableArray<string> a2= a1.Add("java");

上述語句執(zhí)行之后,a1仍然是一個(gè)空集合,a2是包含一個(gè)元素的不變集合??梢枣?zhǔn)降闹貜?fù)調(diào)用Add()方法,最終返回一個(gè)集合:

ImmutableArray<string> a3 = a2.Add("c#").Add("python").Add("php");

在使用不變數(shù)組的每個(gè)階段,都沒有復(fù)制完整的集合。相反,不變類型使用了共享狀態(tài),僅在需要時(shí)復(fù)制集合。

通常,先填充集合,再將它變成不變的數(shù)組會(huì)更高效。當(dāng)需要進(jìn)行一些處理時(shí),可以再次使用可變的集合。

ImmutableList<T>

表示不可變列表,它是可由索引訪問的強(qiáng)類型對(duì)象列表。

示例說明,先定義一個(gè)簡(jiǎn)單的類:

internal class Account
{
    public string Name { get; }
    public decimal Amount { get; }

    public Account(string name, decimal amount)
    {
        this.Name = name;
        this.Amount = amount;
    }
}

接著創(chuàng)建List<Account>集合,使用ToImmutableList方法將其轉(zhuǎn)換為不變的集合。

var accounts = new List<Account>
{
    new Account("圖書",424.2m),
    new Account("文具",1243.5m),
    new Account("籃球",243.3m)
};
//將List轉(zhuǎn)換為不可變集合
ImmutableList<Account> immutableAccounts = accounts.ToImmutableList();
//輸出每一項(xiàng)的內(nèi)容
immutableAccounts.ForEach(a => Console.WriteLine(a.Name + "--" + a.Amount));

如果需要更改不變集合的內(nèi)容,可以使用不變集合的Add、AddRange、Remove、RemoveAt、RemoveRange、Replace以及Sort等方法,這些方法都不是直接改變了原來的不變集合,而是返回一個(gè)新的不可變集合。雖然上述這些方法可以創(chuàng)建新的不變集合,但是如果對(duì)集合頻繁的進(jìn)行多次修改和刪除元素,這就不是非常高效。可以使用ImmutableList<T>ToBuilder() 方法,創(chuàng)建一個(gè)構(gòu)建器,該方法返回一個(gè)可以改變的集合。例如:

 var accounts = new List<Account>
 {
     new Account("圖書",424.2m),
     new Account("文具",1243.5m),
     new Account("籃球",243.3m)
 };
 //先得到不可變集合
 ImmutableList<Account> immutableAccounts = accounts.ToImmutableList();
 //調(diào)用ToBuilder()方法將不可變集合創(chuàng)建為可變集合
 ImmutableList<Account>.Builder builder = immutableAccounts.ToBuilder();
 for (int i = 0; i < builder.Count; i++)
 {
     Account a = builder[i];
     if (a.Amount > 1000)
     {
         builder.Remove(a);
     }
 }
 //將新創(chuàng)建的可變集合調(diào)用ToImmutable()方法得到不可變集合
 ImmutableList<Account> overdrawnAccounts = builder.ToImmutable();
 overdrawnAccounts.ForEach(b => Console.WriteLine(b.Name + "=" + b.Amount));

除了ImmutableArrayImmutableList之外,該命名空間下還提供了其他一些不變的集合類型。如:

  • ImmutableArray<T>ImmutableArray<T>是一個(gè)結(jié)構(gòu),它在內(nèi)部使用數(shù)組類型,當(dāng)不允許更改底層類型,這個(gè)結(jié)構(gòu)實(shí)現(xiàn)了接口IImmutableList<T>。
  • ImmutableList<T>ImmutableList<T>在內(nèi)部使用一個(gè)二叉樹來映射對(duì)象,以實(shí)現(xiàn)接口IImmutableList<T>。
  • ImmutableQueue<T>ImmutableQueue<T> 實(shí)現(xiàn)了接口IImmutableQueue<T> ,允許使用Enqueue、DequeuePeek以先進(jìn)先出的方式訪問元素。
  • ImmutableStack<T>ImmutableStack<T> 實(shí)現(xiàn)了接口IImmutableStack<T>,允許使用Push、PopPeek以先進(jìn)后出的方式訪問元素。
  • ImmutableDictionary<TKey,TValue>ImmutableDictionary<TKey,TValue> 是一個(gè)鍵和值不可變的集合,其無序的鍵/值對(duì)元素實(shí)現(xiàn)了接口IImmutableDictionary<TKey,TValue>
  • ImmutableSortedDictionary<TKey,TValue>ImmutableSortedDictionary<TKey,TValue>是一個(gè)不可變的排序字典。其實(shí)現(xiàn)了接口IImmutableDictionary<TKey,TValue> 。
  • ImmutableHashSet<T> :表示不可變的無序哈希集 ,實(shí)現(xiàn)了接口IImmutableSet<T> 。
  • ImmutableSortedSet<T> :表示不可變的有序集合,實(shí)現(xiàn)了接口IImmutableSet<T> 。

上述的這些不變的集合都實(shí)現(xiàn)了對(duì)應(yīng)的接口,與正常集合相比,這些不變接口的最大區(qū)別是所有改變集合的方法都返回一個(gè)新的集合。

并發(fā)集合

在命名空間System.Collections.Concurrent 中,提供了幾個(gè)線程安全的集合類,線程安全的集合可以防止多個(gè)線程以相互沖突的方式訪問集合。下面列出了System.Collections.Concurrent命名空間中常用的類及其功能。

  • ConcurrentQueue<T> :表示線程安全的先進(jìn)先出(FIFO)集合。 這個(gè)集合類用一種免鎖定的算法實(shí)現(xiàn),使用在內(nèi)部合并到一個(gè)鏈表中的32項(xiàng)數(shù)組。訪問隊(duì)列元素的方法有Enqueue(T) 、TryDequeue(T)TryPeek(T) 。這些方法的命名和前面的Queue<T>類的方法很像,只是給可能調(diào)用失敗的方法加上了前綴Try。因?yàn)檫@個(gè)類實(shí)現(xiàn)了IProducerConsumerCollection<T>接口,所以TryAdd()TryTake()方法僅調(diào)用Enqueue()TryDequeue()方法。
  • ConcurrentStack<T> :表示線程安全的后進(jìn)先出(LIFO)集合。 和ConcurrentQueue<T>類似,只是訪問元素的方法不同。ConcurrentStack<T>類定義了Push(T) 、PushRange()TryPeek(T) 、TryPop(T)TryPopRange(T[])方法。該類也實(shí)現(xiàn)了IProducerConsumerCollection<T>接口。
  • ConcurrentBag<T> :表示線程安全,無序的對(duì)象集合。 該類沒有定義添加或提取項(xiàng)的任何順序。這個(gè)類使用一個(gè)把線程映射到內(nèi)部使用的數(shù)組上的概念,因此嘗試減少鎖定。訪問元素的方法有Add(T)TryPeek(T)TryTake(T) 。該類也實(shí)現(xiàn)了IProducerConsumerCollection<T> 接口。
  • ConcurrentDictionary<TKey,TValue> :表示可以由多個(gè)線程同時(shí)訪問的鍵/值對(duì)的線程安全集合。TryAdd(TKey, TValue) 、 TryGetValue(TKey, TValue) 、TryRemove(TKey, TValue)TryUpdate(TKey, TValue, TValue) 方法以非阻塞的方式訪問成員。因?yàn)樵鼗阪I和值,所以ConcurrentDictionary<TKey,TValue> 類沒有實(shí)現(xiàn)IProducerConsumerCollection<T> 接口。
  • BlockingCollection<T> :為實(shí)現(xiàn)IProducerConsumerCollection線程安全集合提供阻塞和綁定功能。這個(gè)集合可以在添加或提取元素之前,會(huì)阻塞線程并一直等待。 BlockingCollection<T>集合提供了一個(gè)接口,以使用Add(T)Take() 方法來添加和刪除元素。這些方法會(huì)阻塞線程,一直等到任務(wù)可以執(zhí)行為止。Add()方法有一個(gè)重載版本,其中可以給該重載版本傳遞一個(gè)cancellationToken令牌,這個(gè)令牌允許取消被阻塞的調(diào)用。如果不希望線程無限期的等待下去,且不希望從外部取消調(diào)用,就可以使用TryAdd(T)TryTake(T) 方法,在這些方法中,也可以指定一個(gè)超時(shí)值,它表示在調(diào)用失敗之前應(yīng)阻塞線程和等待的最長(zhǎng)時(shí)間。

上述類中,有的實(shí)現(xiàn)了IProducerConsumerCollection<T>接口,IProducerConsumerCollection<T>接口提供了TryAdd(T)TryTake(T) 方法。TryAdd()方法嘗試給集合添加一項(xiàng),返回布爾值;TryTake()方法嘗試從集合中刪除并返回一個(gè)項(xiàng)。

以ConcurrentXXX形式的集合是線程安全的,如果某個(gè)動(dòng)作不適用于線程的當(dāng)前狀態(tài),它們就返回false。在繼續(xù)之前,總是需要確認(rèn)添加或提取元素是否成功。不能相信集合 會(huì)完成任務(wù)。

BlockingCollection<T>是對(duì)實(shí)現(xiàn)了IProducerConsumerCollection<T>接口的任意類的修飾器 ,它默認(rèn)使用ConcurrentQueue<T>類。還可以給構(gòu)造函數(shù)傳遞任何其他實(shí)現(xiàn)了IProducerConsumerCollection<T>接口的類,例如,ConcurrentBag<T>ConcurrentStack<T>。

下面將使用一個(gè)完整的示例說明并發(fā)集合的應(yīng)用。該示例基于管道,即一個(gè)任務(wù)向一個(gè)集合類寫入一些內(nèi)容,同時(shí)另一個(gè)任務(wù)從該集合中讀取內(nèi)容。首先定義一個(gè)基本類:

public class Info
{
    public string Word { get; set; }
    public int Count { get; set; }
    public string Color { get; set; }
    public override string ToString()
    {
        return $"Word:{Word},Count:{Count},Color:{Color}";
    }
}

定義向控制臺(tái)輸出的類,使用同步來避免返回顏色錯(cuò)誤的輸出:

public static class ColoredConsole
{
    private static object syncOutput = new object();

    public static void WriteLine(string message)
    {
        lock (syncOutput)
        {
            Console.WriteLine(message);
        }
    }

    public static void WriteLine(string message, string color)
    {
        lock (syncOutput)
        {
            Console.ForegroundColor = (ConsoleColor)Enum.Parse(
                typeof(ConsoleColor), color);
            Console.WriteLine(message);
            Console.ResetColor();
        }
    }
}

接著定義具體的管道實(shí)現(xiàn),詳細(xì)說明請(qǐng)參加代碼中的注釋:

public static class PipeLineStages
{
    public static Task ReadFilenamesAsync(string path, BlockingCollection<string> output)
    {
        //第一個(gè)階段
        return Task.Factory.StartNew(() =>
        {
            //讀取文件名,并把它們添加到隊(duì)列中
            foreach (string filename in Directory.EnumerateFiles(
                path, "*.cs", SearchOption.AllDirectories))
            {
                //添加到BlockingCollection<T>中
                output.Add(filename);
                ColoredConsole.WriteLine($"stage 1: added {filename}");
            }
            //通知所有讀取器不應(yīng)再等待集合中的任何額外項(xiàng)
            output.CompleteAdding(); //該方法必不可少
        },TaskCreationOptions.LongRunning);
    }

    public static async Task LoadContentAsync(BlockingCollection<string> input, 
        BlockingCollection<string> output)
    {
        //第二個(gè)階段:從隊(duì)列中讀取文件名并加載它們的內(nèi)容,并把內(nèi)容寫入到另一個(gè)隊(duì)列
        //如果不調(diào)用GetConsumingEnumerable()方法,而是直接迭代集合,將不會(huì)迭代之后添加的項(xiàng)
        foreach (var filename in input.GetConsumingEnumerable())
        {
            using (FileStream stream = File.OpenRead(filename))
            {
                var reader = new StreamReader(stream);
                string line = null;
                while ((line = await reader.ReadLineAsync()) != null)
                {
                    output.Add(line);
                    ColoredConsole.WriteLine("stage 2: added " + line);
                }
            }
        }
        output.CompleteAdding();
    }

    public static Task ProcessContentAsync(BlockingCollection<string> input, 
        ConcurrentDictionary<string, int> output)
    {
        return Task.Factory.StartNew(() =>
        {
            //第三個(gè)階段:讀取第二個(gè)階段中寫入內(nèi)容的隊(duì)列,并將結(jié)果寫入到一個(gè)字典中
            foreach (var line in input.GetConsumingEnumerable())
            {
                string[] words = line?.Split(' ', ';', '\t', '{', '}', '(', ')', ':', ',', '"');
                if (words == null) continue;
                foreach (var word in words?.Where(w => !string.IsNullOrEmpty(w)))
                {
                    //如果鍵沒有添加到字典中,第二個(gè)參數(shù)就定義應(yīng)該設(shè)置的值
                    //如果 鍵已經(jīng)存在于字典中,updateValueFactory就定義值的改變方式,++i
                    output.AddOrUpdate(key: word, addValue: 1, 
                                       updateValueFactory: (s, i) => ++i);
                    ColoredConsole.WriteLine("stage 3: added " + word);
                }
            }
        }, TaskCreationOptions.LongRunning);
    }

    public static Task transFerContentAsync(ConcurrentDictionary<string, int> input,
        BlockingCollection<Info> output)
    {
        //第四個(gè)階段:從字典中讀取內(nèi)容,轉(zhuǎn)換數(shù)據(jù),將其寫入隊(duì)列中
        return Task.Factory.StartNew(() =>
        {
            foreach (var word in input.Keys)
            {
                int value;
                if (input.TryGetValue(word, out value))
                {
                    var info = new Info { Word = word, Count = value };
                    output.Add(info);
                    ColoredConsole.WriteLine("stage 4: added " + info);
                }
            }
            output.CompleteAdding();
        }, TaskCreationOptions.LongRunning);
    }

    public static Task AddColorAsync(BlockingCollection<Info> input,
        BlockingCollection<Info> output)
    {
        //第五個(gè)階段:讀取隊(duì)列信息,并添加顏色信息,同時(shí)寫入另一個(gè)隊(duì)列
        return Task.Factory.StartNew(() =>
        {
            foreach (var item in input.GetConsumingEnumerable())
            {
                if (item.Count > 40)
                {
                    item.Color = "Red";
                }
                else if (item.Count > 20)
                {
                    item.Color = "Yellow";
                }
                else
                {
                    item.Color = "Green";
                }
                output.Add(item);
                ColoredConsole.WriteLine("Stage 5: added color " + item.Color + " to " + item);
            }
            output.CompleteAdding();
        }, TaskCreationOptions.LongRunning);
    }

    public static Task ShowContentAsync(BlockingCollection<Info> input)
    {
        //第六個(gè)階段:顯示最終的隊(duì)列信息
        return Task.Factory.StartNew(() =>
        {
            foreach (var item in input.GetConsumingEnumerable())
            {
                ColoredConsole.WriteLine("Stage 6:" + item, item.Color);
            }
        }, TaskCreationOptions.LongRunning);
    }
}

最終的調(diào)用代碼為:

public static async Task StartPipelineAsync()
{
    var fileNames = new BlockingCollection<string>();
    //啟動(dòng)第一個(gè)階段任務(wù),讀取文件名,并寫入到隊(duì)列fileNames中
    Task t1 = PipeLineStages.ReadFilenamesAsync(@"../../", fileNames);
    ColoredConsole.WriteLine("started stage 1");

    var lines = new BlockingCollection<string>();
    //啟動(dòng)第二個(gè)階段任務(wù),將隊(duì)列中的文件名進(jìn)行讀取,獲取該文件的內(nèi)容并寫入到lines隊(duì)列中
    Task t2 = PipeLineStages.LoadContentAsync(fileNames, lines);
    ColoredConsole.WriteLine("started stage 2");

    var words = new ConcurrentDictionary<string, int>();
    //啟動(dòng)第三個(gè)階段任務(wù),讀取lines隊(duì)列中內(nèi)容并寫入到words中
    Task t3 = PipeLineStages.ProcessContentAsync(lines, words);

    //同時(shí)啟動(dòng)1、2、3三個(gè)階段的任務(wù),并發(fā)執(zhí)行
    await Task.WhenAll(t1, t2, t3);
    ColoredConsole.WriteLine("stages 1,2,3 completed");

    var items = new BlockingCollection<Info>();
    //啟動(dòng)第四個(gè)階段任務(wù),將words字典中的數(shù)據(jù)進(jìn)行讀取,寫入到items中
    Task t4 = PipeLineStages.transFerContentAsync(words, items);

    var coloredItems = new BlockingCollection<Info>();
    //啟動(dòng)第五個(gè)階段任務(wù),將items的數(shù)據(jù)進(jìn)行讀取和修改,將結(jié)果寫入到coloredItems中
    Task t5 = PipeLineStages.AddColorAsync(items, coloredItems);

    //啟動(dòng)第六個(gè)階段任務(wù),將最終的結(jié)果顯示出來
    Task t6 = PipeLineStages.ShowContentAsync(coloredItems);

    ColoredConsole.WriteLine("stages 4,5,6 started");
    //同時(shí)啟動(dòng)4、5、6三個(gè)階段的任務(wù)
    await Task.WhenAll(t4, t5, t6);
    ColoredConsole.WriteLine("all sages finished");
}

處理位的集合

如果需要處理的數(shù)字有許多位,可以使用 BitArray類和BitVector32結(jié)構(gòu)。這兩種類型最重要的區(qū)別是:BitArray類可以重新設(shè)置大小,如果事先不知道需要的位數(shù),可以使用BitArray類,它可以包含非常多的位。BitVector32結(jié)構(gòu)是基于棧的,因此比較快。BitVector32結(jié)構(gòu)僅包含32位,它們存儲(chǔ)在一個(gè)整數(shù)中。

BitArray

BitArray 類是一個(gè)引用類型,當(dāng)它的構(gòu)造函數(shù)傳入的是int[]時(shí),每一個(gè)int類型的整數(shù)都將使用32個(gè)連續(xù)位進(jìn)行表示。

public static void Run()
{
    //創(chuàng)建一個(gè)包含8位的數(shù)組,其索引是0~7
    var bits1 = new BitArray(8);
    //把8位都設(shè)置為true
    bits1.SetAll(true);
    //把對(duì)應(yīng)于1的位設(shè)置為false
    bits1.Set(1, false);
    bits1[5] = false;
    bits1[7] = false;
    DisplayBits(bits1); 
    Console.WriteLine(); 

    //Not()方法對(duì)所有的位取反
    bits1.Not();
    DisplayBits(bits1);
    Console.WriteLine();

    var bits2 = new BitArray(bits1);
    bits2[0] = true;
    bits2[1] = false;
    bits2[4] = true;
    DisplayBits(bits1);
    Console.Write (" Or ");
    DisplayBits(bits2);
    Console.Write (" = ");
    //比較兩個(gè)數(shù)組上的同一個(gè)位置上的位,如果有一個(gè)為true,結(jié)果就為true
    bits1.Or(bits2);
    DisplayBits(bits1);
    Console.WriteLine();

    DisplayBits(bits2);
    Console.Write(" and ");
    DisplayBits(bits1);
    Console.Write (" = " );
    //如果兩個(gè)數(shù)組上的同一個(gè)位置的位都為true,結(jié)果才為true
    bits2.And(bits1);
    DisplayBits(bits2);
    Console.WriteLine();

    DisplayBits(bits1);
    Console.Write (" xor ");
    DisplayBits(bits2);
    //比較兩個(gè)數(shù)組上的同一個(gè)位置上的位,只有一個(gè)(不能是二個(gè))設(shè)置為1,結(jié)果才是1
    bits1.Xor(bits2);
    Console.Write(" = ");
    DisplayBits(bits1);
    Console.WriteLine();
}

public static void DisplayBits(BitArray bits)
{
    foreach (bool bit in bits)
    {
        Console.Write(bit ? 1 : 0);
    }
}

BitVector32結(jié)構(gòu)

如果事先知道需要的位數(shù),留可以使用BitVector32 結(jié)構(gòu)代替BitArray類。BitVector32結(jié)構(gòu)效率較高,因?yàn)樗且粋€(gè)值類型,在整數(shù)棧上存儲(chǔ)位。一個(gè)整數(shù)可以存儲(chǔ)32位,如果需要更多的位,就可以使用多個(gè)BitVector32值或BitArray類。BitArray類可以根據(jù)需要增大,但BitVector32結(jié)構(gòu)不能。

BitVector32結(jié)構(gòu)中常用成員:

  • Data :以整數(shù)形式獲取BitVector32的值。Data屬性把BitVector32結(jié)構(gòu)中的數(shù)據(jù)返回為整數(shù)。
  • Item[]BitVector32的值可以使用索引器設(shè)置,索引器是重載的——可以使用掩碼或BitVector32.Section類型的片段來獲取和設(shè)置值。
  • CreateMask() :該方法有多個(gè)重載版本,它是以靜態(tài)方法,用于為訪問BitVector32結(jié)構(gòu)中的特定位創(chuàng)建掩碼。
  • CreateSection(Int16, BitVector32+Section) :該方法有多個(gè)重載版本,也是一個(gè)靜態(tài)方法,用于創(chuàng)建32位中的幾個(gè)片段。
public static void Run()
{
    //使用默認(rèn)構(gòu)造函數(shù)創(chuàng)建一個(gè)BitVactor32結(jié)構(gòu),默認(rèn)每一位都是false。
    var bits1 = new BitVector32();
    //調(diào)用CreateMask()方法創(chuàng)建用來訪問第一位的一個(gè)掩碼,bit1被設(shè)置為1
    int bit1 = BitVector32.CreateMask();
    //再次調(diào)用CreateMask()方法,并將一個(gè)掩碼作為參數(shù)進(jìn)行傳遞,返回第二位掩碼 
    int bit2 = BitVector32.CreateMask(bit1);
    int bit3 = BitVector32.CreateMask(bit2);
    int bit4 = BitVector32.CreateMask(bit3);
    int bit5 = BitVector32.CreateMask(bit4);
    //使用掩碼和索引器訪問位矢量中的位,并設(shè)置值
    bits1[bit1] = true;
    bits1[bit2] = false;
    bits1[bit3] = true;
    bits1[bit4] = true;
    bits1[bit5] = true;
    Console.WriteLine(bits1);
    bits1[0xabcdef] = true;
    Console.WriteLine(bits1);
    
    int received = 0x79abcdef;
    //直接傳入十六進(jìn)制數(shù)來創(chuàng)建掩碼
    BitVector32 bits2 = new BitVector32(received);
    Console.WriteLine(bits2);

    //分割片段
    BitVector32.Section sectionA = BitVector32.CreateSection(0xfff);
    BitVector32.Section sectionB = BitVector32.CreateSection(0xff, sectionA);
    BitVector32.Section sectionC = BitVector32.CreateSection(0xf, sectionB);
    BitVector32.Section sectionD = BitVector32.CreateSection(0x7, sectionC);
    BitVector32.Section sectionE = BitVector32.CreateSection(0x7, sectionD);
    BitVector32.Section sectionF = BitVector32.CreateSection(0x3, sectionE);
    Console.WriteLine("Section A:" + IntToBinaryString(bits2[sectionA], true));
    Console.WriteLine("Section B:" + IntToBinaryString(bits2[sectionB], true));
    Console.WriteLine("Section C:" + IntToBinaryString(bits2[sectionC], true));
    Console.WriteLine("Section D:" + IntToBinaryString(bits2[sectionD], true));
    Console.WriteLine("Section E:" + IntToBinaryString(bits2[sectionE], true));
    Console.WriteLine("Section F:" + IntToBinaryString(bits2[sectionF], true));


}

public static string IntToBinaryString(int bits, bool removeTrailingZero)
{
    var sb = new StringBuilder(32);
    for (int i = 0; i < 32; i++)
    {
        if ((bits & 0x80000000) != 0)
        {
            sb.Append("1");
        }
        else
        {
            sb.Append("0");
        }
        bits = bits << 1;
    }
    string s = sb.ToString();
    if (removeTrailingZero)
    {
        return s.TrimStart('0');
    }
    else
    {
        return s;
    }
}

可觀察的集合

使用ObservableCollection<T> 集合類,可以在集合元素進(jìn)行添加、刪除、移動(dòng)、修改等操作時(shí),提供通知信息。它可以觸發(fā)CollectionChanged 事件,可以在該事件中,進(jìn)行相關(guān)的操作。

public static void Run()
{
    var data = new ObservableCollection<string>();
    data.CollectionChanged += Data_CollectionChanged;
    data.Add("one");
    data.Add("tow");
    data.Insert(1, "Three");
    data.Remove("one");
}

private static void Data_CollectionChanged(object sender,
    System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    Console.WriteLine("action" + e.Action.ToString());

    if (e.OldItems != null)
    {
        Console.WriteLine("OldStartingIndex:" + e.OldStartingIndex);
        Console.WriteLine("old item(s):");
        foreach (var item in e.OldItems)
        {
            Console.WriteLine(item);
        }
    }
    if (e.NewItems != null)
    {
        Console.WriteLine("NewStartingIndex:" + e.NewStartingIndex);
        Console.WriteLine("new items:");
        foreach (var item in e.NewItems)
        {
            Console.WriteLine(item);
        }
    }
    Console.WriteLine();
}

參考資源

本文后續(xù)會(huì)隨著知識(shí)的積累不斷補(bǔ)充和更新,內(nèi)容如有錯(cuò)誤,歡迎指正。

最后一次更新時(shí)間:2018-07-10


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容