文章目录
- 特殊的集合
- BitArray 类
- BitVector32 结构
- 可观察的集合
- 不变的集合
- 并发集合
特殊的集合
BitArray 类
BitArray类是一个引用类型,它包含一个int数组,其中每32位使用一个新整数。

public static class BitArrayExtensions
{
public static string GetBitsFormat(this BitArray bits)
{
var sb = new StringBuilder();
for (int i = bits.Length - 1; i >= 0; i--)
{
sb.Append(bits[i] ? 1 : 0);
if (i != 0 && i % 4 == 0)
{
sb.Append("_");
}
}
return sb.ToString();
}
}
class Program
{
static void Main()
{
var bits1 = new BitArray(9);
bits1.SetAll(true);
bits1.Set(1, false);
bits1[5] = false;
bits1[7] = false;
Console.Write("initialized: ");
Console.WriteLine(bits1.GetBitsFormat());
Console.Write("not ");
Console.Write(bits1.GetBitsFormat());
bits1.Not();
Console.Write(" = ");
Console.WriteLine(bits1.GetBitsFormat());
var bits2 = new BitArray(bits1);
bits2[0] = true;
bits2[1] = false;
bits2[4] = true;
Console.Write($"{bits1.GetBitsFormat()} OR {bits2.GetBitsFormat()}");
Console.Write(" = ");
bits1.Or(bits2);
Console.WriteLine(bits1.GetBitsFormat());
Console.Write($"{bits2.GetBitsFormat()} AND {bits1.GetBitsFormat()}");
Console.Write(" = ");
bits2.And(bits1);
Console.WriteLine(bits2.GetBitsFormat());
Console.Write($"{bits1.GetBitsFormat()} XOR {bits2.GetBitsFormat()}");
bits1.Xor(bits2);
Console.Write(" = ");
Console.WriteLine(bits1.GetBitsFormat());
Console.ReadLine();
}
}
BitVector32 结构
如果事先知道需要的位数,就可以使用BitVector32结构替代BitArray类。BitVector32结构效率较高,因为 它是一个值类型,在整数栈上存储位。一个整数可以存储32位。如果需要更多的位,就可以使用多个BitVector32 值或BitArray类。BitArray类可以根据需要增大,但BitVector32结构不能。

public static class BinaryExtensions
{
public static string AddSeparators(this string number) =>
number.Length <= 4 ? number :
string.Join("_",
Enumerable.Range(0, number.Length / 4)
.Select(i => number.Substring(startIndex: i * 4, length: 4)).ToArray());
public static string ToBinaryString(this int number) =>
Convert.ToString(number, toBase: 2).AddSeparators();
}
class Program
{
static void Main()
{
var bits1 = new BitVector32();
int bit1 = BitVector32.CreateMask();
int bit2 = BitVector32.CreateMask(bit1);
int bit3 = BitVector32.CreateMask(bit2);
int bit4 = BitVector32.CreateMask(bit3);
int bit5 = BitVector32.CreateMask(bit4);
bits1[bit1] = true;
bits1[bit2] = false;
bits1[bit3] = true;
bits1[bit4] = true;
bits1[bit5] = true;
Console.WriteLine(bits1);
bits1[0xab_cdef] = true;
Console.WriteLine();
int received = 0x79ab_cdef;
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: {bits2[sectionA].ToBinaryString()}");
Console.WriteLine($"Section B: {bits2[sectionB].ToBinaryString()}");
Console.WriteLine($"Section C: {bits2[sectionC].ToBinaryString()}");
Console.WriteLine($"Section D: {bits2[sectionD].ToBinaryString()}");
Console.WriteLine($"Section E: {bits2[sectionE].ToBinaryString()}");
Console.WriteLine($"Section F: {bits2[sectionF].ToBinaryString()}");
Console.ReadLine();
}
}
可观察的集合
如果需要集合中的元素何时删除或添加的信息,就可以使用ObservableCollectionO类。这个类最初是为
WPF定义的,这样UI就可以得知集合的变化,通用Windows应用程序使用它的方式相同。这个类的名称空间 是System.Collections. ObjectModeloObservableCollection类派生自Collection基类,该基类可用于创建自定义集合,并在内部使用Ust 类。重写基类中的虚方法SetltonO和RemoveltonO*以触发CollectionChanged事件。这个类的用户可以使用 INotifyCollectionChanged 接口注册这个事件。
class Program
{
static void Main()
{
var data = new ObservableCollection<string>();
data.CollectionChanged += Data_CollectionChanged;
data.Add("One");
data.Add("Two");
data.Insert(1, "Three");
data.Remove("One");
data.CollectionChanged -= Data_CollectionChanged;
Console.ReadLine();
}
public static void Data_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Console.WriteLine($"action: {e.Action.ToString()}");
if (e.OldItems != null)
{
Console.WriteLine($"starting index for old item(s): {e.OldStartingIndex}");
Console.WriteLine("old item(s):");
foreach (var item in e.OldItems)
{
Console.WriteLine(item);
}
}
if (e.NewItems != null)
{
Console.WriteLine($"starting index for new item(s): {e.NewStartingIndex}");
Console.WriteLine("new item(s): ");
foreach (var item in e.NewItems)
{
Console.WriteLine(item);
}
}
Console.WriteLine();
}
}
不变的集合
如果对象可以改变其状态,就很难在多个同时运行的任务中使用。这些集合必须同步。如果对象不能改变 其状态,就很容易在多个线程中使用。不能改变的对象称为不变的对象。不能改变的集合称为不变的集合。
public class Account
{
public Account(string name, decimal amount)
{
Name = name;
Amount = amount;
}
public string Name { get; }
public decimal Amount { get; }
}
class Program
{
static void Main()
{
SimpleArrayDemo();
ImmutableList<Account> accounts = CreateImmutableList();
UsingABuilder(accounts);
LinqDemo();
Console.ReadLine();
}
public static void LinqDemo()
{
ImmutableArray<string> arr = ImmutableArray.Create<string>("one", "two", "three", "four", "five");
var result = arr.Where(s => s.StartsWith("t"));
}
public static void UsingABuilder(ImmutableList<Account> immutableAccounts)
{
ImmutableList<Account>.Builder builder = immutableAccounts.ToBuilder();
for (int i = 0; i < builder.Count; i++)
{
Account a = builder[i];
if (a.Amount > 0)
{
builder.Remove(a);
}
}
ImmutableList<Account> overdrawnAccounts = builder.ToImmutable();
overdrawnAccounts.ForEach(a => Console.WriteLine($"{a.Name} {a.Amount}"));
}
public static ImmutableList<Account> CreateImmutableList()
{
var accounts = new List<Account>() {
new Account("Scrooge McDuck", 667377678765m),
new Account("Donald Duck", -200m),
new Account("Ludwig von Drake", 20000m)
};
ImmutableList<Account> immutableAccounts = accounts.ToImmutableList();
foreach (var account in immutableAccounts)
{
Console.WriteLine($"{account.Name} {account.Amount}");
}
immutableAccounts.ForEach(a => Console.WriteLine($"{a.Name} {a.Amount}"));
return immutableAccounts;
}
public static void SimpleArrayDemo()
{
ImmutableArray<string> a1 = ImmutableArray.Create<string>();
ImmutableArray<string> a2 = a1.Add("Williams");
ImmutableArray<string> a3 =
a2.Add("Ferrari").Add("Mercedes").Add("Red Bull Racing");
}
}
并发集合
不变的集合很容易在多个线程中使用,因为它们不能改变。如果希望使用应在多个线程中改变的集合,.NET 在名称空间System.Collections.Concurrent中提供了几个线程安全的集合类。线程安全的集合可防止多个线程以 相互冲突的方式访问集合。
为了对集合进行线程安全的访问,定义了 IProducerConsumerCollection接口。这个接口中最重要的方法是 TryAdd()和TryTake()。TryAdd()方法尝试给集合添加一项,但如果集合禁止添加项,这个操作就可能失败。为了给 出相关信息,TiyAdd()方法返回一个布尔值,以说明操作是成功还是失败。TryTake()方法也以这种方式工作,以通 知调用者操作是成功还是失败,并在操作成功时返回集合中的项。
public 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();
}
}
}
public class Info
{
public Info(string word, int count)
{
Word = word;
Count = count;
}
public string Word { get; }
public int Count { get; }
public string Color { get; set; }
public override string ToString() => $"{Count} times: {Word}";
}
public static class PipelineStages
{
public static Task ReadFilenamesAsync(string path, BlockingCollection<string> output)
{
return Task.Factory.StartNew(() =>
{
foreach (string filename in Directory.EnumerateFiles(path, "*.cs",
SearchOption.AllDirectories))
{
output.Add(filename);
ColoredConsole.WriteLine($"stage 1: added {filename}");
}
output.CompleteAdding();
}, TaskCreationOptions.LongRunning);
}
public static async Task LoadContentAsync(BlockingCollection<string> input, BlockingCollection<string> output)
{
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}");
await Task.Delay(20);
}
}
}
output.CompleteAdding();
}
public static Task ProcessContentAsync(BlockingCollection<string> input, ConcurrentDictionary<string, int> output)
{
return Task.Factory.StartNew(() =>
{
foreach (var line in input.GetConsumingEnumerable())
{
string[] words = line.Split(' ', ';', '\t', '{', '}', '(', ')',
':', ',', '"');
foreach (var word in words.Where(w => !string.IsNullOrEmpty(w)))
{
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)
{
return Task.Factory.StartNew(() =>
{
foreach (var word in input.Keys)
{
if (input.TryGetValue(word, out int value))
{
var info = new Info(word, value);
output.Add(info);
ColoredConsole.WriteLine($"stage 4: added {info}");
}
}
output.CompleteAdding();
}, TaskCreationOptions.LongRunning);
}
public static Task AddColorAsync(BlockingCollection<Info> input, BlockingCollection<Info> output)
{
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)
{
return Task.Factory.StartNew(() =>
{
foreach (var item in input.GetConsumingEnumerable())
{
ColoredConsole.WriteLine($"stage 6: {item}", item.Color);
}
}, TaskCreationOptions.LongRunning);
}
}
class Program
{
static void Main()
{
StartPipelineAsync().Wait();
Console.ReadLine();
}
public static async Task StartPipelineAsync()
{
var fileNames = new BlockingCollection<string>();
var lines = new BlockingCollection<string>();
var words = new ConcurrentDictionary<string, int>();
var items = new BlockingCollection<Info>();
var coloredItems = new BlockingCollection<Info>();
Task t1 = PipelineStages.ReadFilenamesAsync(@"../..", fileNames);
ColoredConsole.WriteLine("started stage 1");
Task t2 = PipelineStages.LoadContentAsync(fileNames, lines);
ColoredConsole.WriteLine("started stage 2");
Task t3 = PipelineStages.ProcessContentAsync(lines, words);
await Task.WhenAll(t1, t2, t3);
ColoredConsole.WriteLine("stages 1, 2, 3 completed");
Task t4 = PipelineStages.TransferContentAsync(words, items);
Task t5 = PipelineStages.AddColorAsync(items, coloredItems);
Task t6 = PipelineStages.ShowContentAsync(coloredItems);
ColoredConsole.WriteLine("stages 4, 5, 6 started");
await Task.WhenAll(t4, t5, t6);
ColoredConsole.WriteLine("all stages finished");
}
}