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));
除了ImmutableArray和ImmutableList之外,該命名空間下還提供了其他一些不變的集合類型。如:
-
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、Dequeue和Peek以先進(jìn)先出的方式訪問元素。 -
ImmutableStack<T>:ImmutableStack<T>實(shí)現(xiàn)了接口IImmutableStack<T>,允許使用Push、Pop和Peek以先進(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();
}
參考資源
- 《C#高級(jí)編程(第10版)》
- C#不變集合
- C#并發(fā)集合
- C#可觀察集合
- C#位數(shù)組
- C# BitVector32結(jié)構(gòu)
本文后續(xù)會(huì)隨著知識(shí)的積累不斷補(bǔ)充和更新,內(nèi)容如有錯(cuò)誤,歡迎指正。
最后一次更新時(shí)間:2018-07-10