C# in Depth

C# in Depth

The changing face of C# development

1.1 Starting with a simple data type

1.1.1 The Product type in C#1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System.Collections;
using System.ComponentModel;

namespace Chapter01.CSharp1
{
[Description("Listing 1.01")]
public class Product
{
string name;
public string Name
{
get { return name; }
}

decimal price;
public decimal Price
{
get { return price; }
}

public Product(string name, decimal price)
{
this.name = name;
this.price = price;
}

public static ArrayList GetSampleProducts()
{
ArrayList list = new ArrayList();
list.Add(new Product("West Side Story", 9.99m));
list.Add(new Product("Assassins", 14.99m));
list.Add(new Product("Frogs", 13.99m));
list.Add(new Product("Sweeney Todd", 10.99m));
return list;
}

public override string ToString()
{
return string.Format("{0}: {1}", name, price);
}
}
}

1.1.2 Strongly typed collections in C#2

The most important change in C# 2: generics

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TheProductType.cs" company="psmyfish.top">
// pp
// </copyright>
// <summary>
// Defines the TheProductType type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace C01
{
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;

/// <summary>
/// The the product type.
/// </summary>
[Description("Listing 1.01")]
public class Product
{
/// <summary>
/// The name.
/// </summary>
private string name;

/// <summary>
/// Gets the name.
/// </summary>
public string Name
{
get
{
return name;
}
private set
{
name = value;
}
}

/// <summary>
/// The price.
/// </summary>
private decimal price;

/// <summary>
/// Gets the price.
/// </summary>
public decimal Price
{
get
{
return price;
}
private set
{
price = value;
}
}

/// <summary>
/// Initializes a new instance of the <see cref="Product"/> class.
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <param name="price">
/// The price.
/// </param>
public Product(string name, decimal price)
{
Name = name;
Price = price;
}

/// <summary>
/// The get sample products.
/// </summary>
/// <returns>
/// The <see cref="ArrayList"/>.
/// </returns>
public static List<Product> GetSampleProducts()
{
List<Product> list = new List<Product>();
list.Add(new Product("West Side Story", 9.99m));
list.Add(new Product("Assassins", 14.99m));
list.Add(new Product("Frogs", 13.99m));
list.Add(new Product("Sweeney Todd", 10.99m));
return list;
}

/// <summary>
/// The to string.
/// </summary>
/// <returns>
/// The <see cref="string"/>.
/// </returns>
public override string ToString()
{
return string.Format("{0}:{1}", name, price);
}
}
}

1.1.3 Automatically implemented properties in C#3

The automatically implemented properties and simplified initialization shown in the following listing are relatively trivial.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
namespace C01
{
using System.Collections.Generic;

class Product
{
public string Name { get; private set; }
public decimal Price { get; private set; }

public Product(string name, decimal price)
{
Name = name;
Price = price;
}
Product() { }

public static List<Product> GetSampleProducts()
{
return new List<Product>
{
new Product { Name = "West", Price = 9.99m },
new Product { Name = "Assassins", Price = 14.99m },
new Product { Name = "Frogs", Price = 13.99m },
new Product { Name = "Sweeney Todd", Price = 10.99m }
};
}

public override string ToString()
{
return string.Format("{0},{1}", Name, Price);
}
}
}

1.1.4 Name arguments in C# 4

This gives you the clarity of C# 3 initializers without the mutability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System.Collections.Generic;

class Product
{
readonly string name;
public string Name { get{ return name; } }

private readonly decimal price;
public decimal Price { get{ return price; } }

public Product(string name, decimal price)
{
this.name = name;
this.price = price;
}

public static List<Product> GetSampleProducts()
{
return new List<Product>
{
new Product(name: "West", price: 9.99m),
new Product(name:"Assassins", price: 14.99m),
new Product(name:"Frogs", price: 13.99m),
new Product(name:"Sweeney Todd", price: 10.99m)
};
}

public override string ToString()
{
return string.Format("{0},{1}", Name, Price);
}
}

Here is the summarizes.

001

1.2 Sorting and filtering

1.2.1 Sorting products by name

if you use this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Collections;
using System.ComponentModel;

[Description("Listing 1.05")]
class ArrayListSort
{
class ProductNameComparer : IComparer
{
public int Compare(object x, object y)
{
Product first = (Product)x;
Product second = (Product)y;
return first.Name.CompareTo(second.Name);
}
}

static void Main()
{
ArrayList products = Product.GetSampleProducts();
products.Sort(new ProductNameComparer());
foreach (Product product in products)
{
Console.WriteLine(product);
}
}
}

Casts are used but you cant ensure that you are right.The (Product) & cw(product) have a chance to go bang.

But if you use Generic as the only change.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[Description("Listing 1.05")]
class ArrayListSort
{
class ProductNameComparer : IComparer<Product>
{
public int Compare(Product x,Product y)
{
return x.Name.CompareTo(y.Name);
}
}

static void Main()
{
List<Product> products = Product.GetSampleProducts();
products.Sort(new ProductNameComparer());
foreach (Product product in products)
{
Console.WriteLine(product);
}
}
}

The following listing shows how to do precisely this,telling the Sort method how to compare two products using a delegate.

1
2
3
4
5
6
7
8
9
10
11
12
13
class ArrayListSort
{
static void Main()
{
List<Product> products = Product.GetSampleProducts();
products.Sort(delegate(Product x, Product y)
{ return x.Name.CompareTo(y.Name); });
foreach (Product product in products)
{
Console.WriteLine(product);
}
}
}

Behold the lack of the ProductNameComparer type.

Listing 1.8 Sorting using Comparison from a lambda expression

1
products.Sort((x,y)=>x.Name.CompareTo(y.Name));

There is more, though: with C# 3, you can sasily print out the names in order without modifying the original list of products.The next listing shows this using the OrderBy method.

Listing 1.9 Ordering a List using an extension method(C# 3)

1
2
3
4
5
List<Product> products = Product.GetSampleProducts();
foreach (Product product in products.OrderBy(p =>p.Name))
{
Console.WriteLine(product);
}

You’re able to call the OrderBy method due to the presence of an extension method.

003

1.2.2 Querying collections

Your next task is to find all the elements of the list that match a certain criterion- in particular ,those with a price greater than $10.

Listing 1.10 Looping,testing,printing out( C# 1)

1
2
3
4
5
6
7
8
ArrayList products= Product.GetSampleProducts();
foreach(Product product in products)
{
if(product.Price>10m)
{
Console.WriteLine(product);
}
}

The following listing demonstrates how C#2 lets you flatten things out a bit.

Listing 1.11 Separating testing from printing (C# 2)

1
2
3
4
5
List<Product> products = Product.GetSampleProducts();
Predicate<Product> test = delegate(Product p) { return p.Price > 10m; };
List<Product> matches = products.FindAll(test);
Action<Product> print = Console.WriteLine;
matches.ForEach(print);

The print variable initialization uses another new C# 2 feature called method group conversions that make it easier to create delegates from existing methods.

Listing 1.12 Separating testing from printing redux(C# 2)

1
2
3
List<Product> products = Product.GetSampleProducts();
products.FindAll(delegate(Product p) { return p.Price > 10; }).
ForEach(Console.WriteLine);

Listing 1.13 Testing with a lambda expression(C# 3)

1
2
3
4
5
List<Product> products = Product.GetSampleProducts();
foreach(Product product in products.Where(p=>p.Price>10))
{
Console.WriteLine(product);
}

1.3.1 Representing an unknown price

.NET 2.0 makes matters a lot simpler by introducing the Nullable structure, and C# 2 provides some additional syntactic sugar that lets you change the property declaration to this block of code:

1
2
3
4
5
6
decimal?price;
public decimal?Price
{
get{return price;}
private set {price= value;}
}

Listing 1.14 Displaying products with an unknown price(C# 3)

1
2
3
4
5
List<Product> products = Product.GetSampleProducts();
foreach(Product product in products.Where(p=>p.Price==null))
{
cw(product.Name);
}

the C# 2 code

1
2
List<Product> products = Product.GetSampleProducts();
products.FindAll(delegate(Product p){return p.Price==null;}).ForEach(Console.WriteLine);

1.3.2 Optional parameters and default values

optional parameters

let’s suppose that most of the products dont have prices. It would be nice to be able to initialize a product like this:

1
Product p = new Product("Unreleased product");

C#4 allows your to declare a default value for the price parameter

1
2
3
4
5
public Product(string name,decimal? price=null)
{
this.name=name;
this.price= price;
}

1.4 Introducing LINQ

1.4.1 Query expressions and in-process queries

Listing 1.15 First steps with query expressions:filtering acollection

1
2
3
4
5
6
7
8
List<Product> products = Product.GetSampleProducts();
var filtered = from Product p in products
where p.Price > 10
select p;
foreach (Product product in filtered)
{
Console.WriteLine(product);
}

Listing 1.16

1
2
3
4
5
6
7
8
9
10
11
12
List<Product> products = Product.GetSampleProducts();
List<Supplier> suppliers = Supplier.GetSampleSuppliers();
var filtered = from p in products
join s in suppliers
on p.SupplierID equals s.SupplierID
where p.Price > 10
orderby s.Name, p.Name
select new { SupplierName = s.Name, ProductName = p.Name };
foreach (var v in filtered)
{
Console.WriteLine("Supplier={0},Product={1}", v.SupplierName, v.ProductName);
}

Listing 1.19

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using Microsoft.Office.Interop.Excel;
var app = new Application { Visible = false };
Workbook workbook = app.Workbooks.Add();
Worksheet worksheet = app.ActiveSheet;
int row = 1;
foreach (var product in Product.GetSampleProducts()
.Where(p => p.Price != null))
{
worksheet.Cells[row, 1].Value = product.Name;
worksheet.Cells[row, 2].Value = product.Price;
row++;
}
workbook.SaveAs(Filename: "demo.xls",
FileFormat: XlFileFormat.xlWorkbookNormal);
app.Application.Quit();

Core foundations:building on C#1

2.1 Delegates

委托不需要直接指定一个要执行的行为,而是将这个行为用某种方式“包含”在一个对象中。。这个对象可以像其他任何对象那样使用。在该对象中,可以执行封装的操作。

可以选择将委托类型看作只定义了一个方法的接口,将委托的实例看做实现了哪一个接口的一个对象。

2.1.1 A recipe for simple delegates

委托成立条件

  • The delegate type needs to be declared.
  • The code to be executed must be contained in a method
  • A delegate instance must be created
  • The delegate instance must be invoked.

声明:delegate void StringProcessor(string input)

Note:区分委托类型和 委托实例

创建一个StringProcessor实例的两个例子:

1
2
3
4
StringProcessor proc1,proc2;
proc1= new StringProcessor(StaticMethods.PrintString);
InstanceMethods instance = new InstanceMethods();
proc2 = new StringProcessor(instance.PrintString);

调用委托实例

void Invoke(string input)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;;
delegate void StringProcessor(string input);
class Person{
string name;
public Person(string name){this.name=name;}
public void Say(string message)
{
Console.WriteLine("{0} says:{1}",name,message);
}
}

class Background
{
public static void Note(string note)
{
Console.WriteLine("({0})",note);
}
}
class SimpleDelegateUse
{
static void Main()
{
Person jon= new Person("Jon");
Person tom = new Person("Tom");
StringProcessor jonsVoice,tomsVoice,backgrond;
jonsVoice = new StringProcessor(jon.Say);
tomsVoice = new StringProcessor(tom.Say);
backgrond = new StringProcessor(Background.Note);
jonsVoice("Hello,son.");
tomsVoice.Invoke("Hello,Daddy!");
backgrond("An airplane flied past.");
}
}

2.1.2 Combining and removing delegates

委托实例的调用列表i(invocation list)System.Delegate类型的静态方法Combine 和Remove。

Note:委托是不易变的 创建了委托实例后,有关它的一切就不能改变。这样一来就可以安全地传递委托实例的引用。委托和string是一样的。Delegate.Combine 和String.Concat很像。如果把null和委托实例合并到一起,null将被视为带有空调用列表的一个委托

Delegate.Combine很少显式调用,一般用+ 和+=

Invoke 的返回值是最后一个操作的返回值

如果中间有异常,会立刻“传播”

2.1.3 A brief diversion into events

事件不是委托类型的字段。对于一个纯粹的事件,你所能做的就是订阅(add) 和取消订阅(remove).

2.1.4 Summary of delegates

  • 委托封装了包含特殊返回类型和一组参数的行为,类似包含单一方法的接口
  • 委托类型声明中所描述的类型签名决定了哪个方法可以用于创建委托实例,同时决定了调用的签名
  • 为了创建委托实例,需要一个方法以及(对于实例方法来说)调用方法的目标
  • 委托实例是不易变的
  • 每个委托实例都包含一个调用列表– 一个操作列表
  • 委托实例可以合并到一起,也可以从一个委托实例中删除另一个
  • 事件不是委托实例–只是成对的add/remove方法(类似于属性的取值方法/赋值方法)

2.2 Type system characteristics

现在被称为的强/弱、安全/不安全、静态/动态语言的特征

2.2.1 C#’s place in the world of type systems

C# 1’s type system is static ,explicit and safe.

很多书和文章将C#描述成强类型的语言,实际都是指它是一种静态类型的语言

STATIC TYPING VERSUS DYNAMIC TYPING

看这个强制生效的例子

1
2
3
4
object o ="hello";
Console.WriteLine(o.Length);
object o ="hello";
Console.WriteLine(((String)o).Length);

可以说o的静态类型为System.Object.

动态类型的实质是变量中含有值,但是那些值并不限于特定的类型,所以编译器不能执行相同形式的检查。

对一个动态语言 可以

1
2
3
4
o="hello";
Console.WriteLine(o.Length);
o= new string[]{"hi","there"};
Console.WriteLine(o.Length);
EXPLICIT TYPING VERSUS IMPLICIT TYPING
1
2
3
var s="hello"
var x=s.Length;
var twiceX=x*2;

这在C#1中是不行的

TYPE-SAFE VERSUS TYPE-UNSAFE

C 例子

1
2
3
4
5
6
7
#include<stdio.h>
int main(int argc,char**argv)
{
char *first_arg= argv[1];
int *first_arg_as_int =(int *)first_arg;
printf("%d",*first_arg_as_int);
}

2.2.2 When is C# 1’s type ststem not rich enough?

?:skier:

  1. 集合,强和弱
  2. 缺乏协变的返回类型

2.2.3 Summary of type system characteristics

  • C#1 是静态类型的–编译器知道你能使用哪些成员
  • C#1 是显式的–必须告诉编译器变量具有什么 类型
  • C#1 是安全的–除非存在真实的转换关系,否则不能将一种类型当作另外一种类型
  • 静态类型仍然不允许一个集合称为强类型的 字符串列表 或者 整数列表 除非针对不同的元素使用大量的重复代码
  • 方法覆盖和接口实现不允许协变性和逆变性

2.3 Value types and reference types

2.3.1 Values and references in the real world

读报纸和读网页,对应的值类型和引用类型的区别

类是引用类型 而结构是值类型

特殊情况:

  • 数组类型是引用类型
  • 枚举是值类型
  • 委托类型是引用类型
  • 接口类型是引用类型

2.3.2 Value and reference type fundamentals

变量的值在它声明时的位置存储。局部变量的值总是存储在栈中stack。实例变量的值总是存储在实例本身存储的地方。引用类型实例(对象)总是存储在堆中heap,静态变量也是

2.3.3 Dispelling myths

误区1:“结构是轻量级的类”

2.3.4 Boxing and unboxing

1
2
3
int i=5;
object o =i;
int j =(int)o;

第二句装箱 第三局拆箱

装拆都不会改变原始值

将值作为接口表达式时也会装箱

IComparable x=5;

2.3.5 值类型和引用类型小结

  • 对于引用类型的表达式,它的值是一个引用,而非对象
  • 引用就像URL-是允许你访问真实信息的一小片数据
  • 对于值类型的表达式,他的值就是实际的数据
  • 有时,值类型比引用类型更有效,有时恰好相反
  • 引用类型的对象总是在堆上,值类型的值既可能在堆上 也可能在栈上
  • 引用类型作为方法参数使用时,参数默认是以值传递 方式来传递的–但值本身是一个引用
  • 值类型的值会在需要引用类型的行为时被装箱;拆箱是相反的过程。

2.4 C#1之外:构建于坚实基础之上的新特性

2.4.1 与委托有关的特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void HandleDemoEvent(object sender,EventArgs e)
{
System.Console.WriteLine("Handled by HandleDemoEvent");
}
...
EventHandler handler;
handler = new EventHandler(HandleDemoEvent);
handler(null, EventArgs.Empty);

handler= HandleDemoEvent;
handler(null, EventArgs.Empty);
handler = delegate(object sender,EventArgs e)
{
Console.WriteLine("Handled anonymously:");
};
handler(null,EventArgs.Empty);
handler=delegate
{
Console.WriteLine("handled anonymously again");
};
handler(null,EventArgs.Empty);
MouseEventHandler mouseHandler = HandleDemoEvent;
mouseHandler(null,new MouseEventArgs(MouseButtons.None,0,0,0,0));

Lambda表达式

1
2
Func<int ,int,string > func =(x,y)=>(x*y).ToString();
Console.WriteLine(func(5,20));

###2.4.2 Features related to the type system

匿名类型和隐式类型

1
2
3
4
var jon = new {Name ="Jon",Age = 31};
var tom = new {Name ="Tom",Age =4};
Console.WriteLine("{0} is {1}",jon.Name,jon.Age);
Console.WriteLine("{0} is {1}",tom.Name,tom.Age);

C#4中的动态类型

1
2
3
4
dynamic o ="hello";
Console.WriteLine(o.Length);
o = new String[] {"hi","there"};
Console.WriteLine(o.Length);
  • 泛型
  • 可空类型
1
2
3
4
5
6
7
8
int? x= null;
x = 5;
if(x!=null)
{
int y = x.value;
Console.WriteLine(y);
}
int z = x??10;

###2.5 Summary

3 Parameterized typing with generics

3.1 why generics are necessary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static Dictionary<string,int> CountWords(string text)
{
Dictionary<string,int> frequencies;
frequencies = new Dictionary<string,int>();
string[] words = Regex.Split(text,@"\W+");
foreach(string word in words)
{
if(frequencies.ContainsKey(word))
{
frequencies[word]++;
}
else
frequencies[word]=1;
}
return frequencies;
}
...
string text = @"Do you like green eggs and ham?
I do not like them,Sam-I-am.
I do not like green eggs and ham.";
Dictionary<string ,int> frequencies= CountWords(text);
foreach(KeyValuePair<string,int> entry in frequencies)
{
string word = entry.Key;
int frequency = entry.Value;
Console.WriteLine ("{0}:{1}",word,frequency);
}

3.2.2 Generic types and type parameters

泛型有两种形式: 泛型类型和泛型方法,两者都是表示API的基本方法.

类型参数是真实类型的占位符,真实的类型来替代他们,称为类型实参.

泛型类型中的方法签名 类型参数被替换之后的方法签名
void Add(TKey key,TValue value) void Add(string Key,int value)
TValue this[TKey key]{get;set;} int this[string key]{get;set;}
bool ContainsValue(TValue value) bool ContainsValue(int value)
bool ContainsKey(TKey key) bool ContainsKey(string key)

3.2.3 Generic methods and reading generic declarations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static double TakeSquareRoot(int x)
{
return Math.Sqrt(x);
}
...
List<int> integers = new List<int>();
integers.Add(1);
integers.Add(2);
integers.Add(3);
integers.Add(4);
Converter<int,double> converter = TakeSquareRoot;
List<double> doubles;
doubles = integers.ConvertAll<double>(converter);
foreach(double d in doubles)
{
Console.WriteLine(d);
}

Listing 3.3

1
2
3
4
5
6
7
8
9
10
11
12
13
static List<T> MakeList<T> (T first,T second)
{
List<T> list = new List<T>();
list.Add(first);
list.Add(second);
return list;
}
...
List<string> list = MakeList<string>("Line 1","Line 2");
foreach(string x in list)
{
Console.WriteLine(x);
}

###3.3 Beyond the basics

  • type constraints
  • type inference

3.3.1 Type constraints

REFERENCE TYPE CONSTRAINT

struct RefSample<T> where T: class

Valid closed types using this declaration include

  • RefSample<IDisposable>
  • RefSample<string>
  • RefSample<int[]>

Invalid closed types include

  • RefSample<Guid>
  • RefSample<int>

VALUE TYPE CONSTRAINTS

class ValSample<T> where T:struct

Valid closed types include

  • ValSample<int>
  • ValSample<FileMode>

Invalid closed types include

  • ValSample<object>
  • ValSample<StringBuilder>

CONSTRUCTOR TYPE CONSTRAINTS

1
2
3
4
public T CreateInstance<T>() where T: new()
{
return new T();
}

Valid : CreateInstance<int>() CreateInstance<object>

CONVERSION TYPE CONSTRAINTS

1
2
3
class Sample<T> where T: Stream,
IEnumerable<string>,
IComparable<int>

可以指定多个接口,但是只能指定一个类.

COMBINING CONSTRAINTS

Valid:

1
2
3
4
class Sample<T> where T:class,IDisposable,new()
class Sample<T> where T:struct,IDisposable
class Sample<T,U> where T: class where U:struct,T
class Sample<T,U> where T: Stream where U:IDisposable

Invalid:

1
2
3
class Sample<T> where T:class,struct
class Sample<T> where T: Stream,class
class Sample<T,U> where T:Stream,U:IDisposable

3.3.2 Type inference for type arguments of generic methods

1
2
3
4
5
static List<T> MakeList<T>(T first,T second)
...
List<string> list = MakeList<string>("Line 1","Line2");
//we can write
List<string> list = MakeList("Line 1","Line 2");

3.3.3 Implementing generics

DEFAULT VALUE EXPRESSIONS

listing 3.4 Comparing a given value to the default in a generic way

1
2
3
4
5
6
7
8
9
10
static int CompareToDefault<T> (T value) where T:IComparable<T>
{
return value.CompareTo(default(T));
}
...
Console.WriteLine(CompareToDefault("x"));
Console.WriteLine(CompareToDefault(10));
Console.WriteLine(CompareToDefault(0));
Console.WriteLine(CompareToDefault(-10));
Console.WriteLine(CompareToDefault(DateTime.MinValue));

DIRECT COMPARISONS

when a type parameter is unconstrained , you can use the == and != operators,but ony to compare a value of that type with null; you cant compare two values of type T with each other.

1
2
3
4
5
6
7
8
9
10
11
static bool AreReferencesEqual<T>(T first ,T second) 
where T: class
{
return first == second;
}
...
string name ="Jon";
string intro1 = "my name is "+ name;
string intro2 = "my name is "+ name;
Console.WriteLine(intro1==intro2);
Console.WriteLine(AreReferencesEqual(intro1,intro2));

FULL COMPARISON EXAMPLE:REPRESENTING A PAIR OF VALUES

Listing 3.6 Generic class representing a pair of values

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
public sealed class Pair<T1,T2>:IEquatable<Pair<T1,T2>>
{
private static readonly IEqualityComparer<T1> FirstComparer =
EqualityComparer<T1>.Default;
private static readonly IEqualityComparer<T2> SecondComparer =
EqualityComparer<T2>.Default;
private readonly T1 first;
private readonly T2 second;
public Pair(T1 first ,T2 second)
{
this.first = first;
this.second = second;
}
public T1 First { get { return first; } }
public T2 Second { get { return second; } }
public bool Equals(Pair<T1,T2> other)
{
return other != null &&
FirstComparer.Equals(this.First, other.First) &&
SecondComparer.Equals(this.Second, other.Second);
}
public override bool Equals(object obj)
{
return Equals(obj as Pair<T1,T2>);
}
public override int GetHashCode()
{
return FirstComparer.GetHashCode(first) * 37 +
SecondComparer.GetHashCode(second);
}
}

public static class Pair
{
public static Pair<T1,T2> Of<T1,T2>(T1 first ,T2 second)
{
return new Pair<T1, T2>(first, second);
}
}

class Program
{
static void Main(string[] args)
{
Pair<int, string> pair = new Pair<int, string>(10, "value");
Pair<int, string> pair1 = Pair.Of(10, "value");
Console.WriteLine(pair.First);
Console.WriteLine(pair.Second);
Console.WriteLine(pair1.First);
Console.WriteLine(pair1.Second);
}
}
}

3.4 Advanced generics

3.4.1 Static fields and static constructors

Listing 3.8 Proof that different closed type s have different static fields

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class TypeWithField<T>
{
public static string field;
public static void PrintField()
{
Console.WriteLine(field+":"+typeof(T).Name);
}
}
...
TypeWithField<int>.field ="First";
TypeWithField<string>.field ="second";
TypeWithField<DateTime>.field ="Third";
TypeWithField<int>.PrintField();
TypeWithField<string>.PrintField();
TypeWithField<DateTime>.PrintField();

Listing 3.9 Static constructors with nested generic types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Outer<T>
{
public class Inner<U,V>
{
static Inner()
{
Console.WriteLine("Outer<{0}>.Inner<{1},{2}>",
typeof(T).Name,
typeof(U).Name,
typeof(V).Name
);
}
public static void DummyMethod(){}
}
}
...
Outer<int>.Inner<string,DateTime>.DummyMethod();
Outer<string>.Inner<int,int>.DummyMethod();
Outer<object>.Inner<string,object>.DummyMethod();
Outer<string>.Inner<string,object>.DummyMethod();
Outer<object>.Inner<object,string>.DummyMethod();
Outer<string>.Inner<int,int>.DummyMethod();

3.4.2 How the JIT compiler handles generics

3.4.3 Generic iteration

listing 3.10 A full generic iterator - of the numbers 0 to 9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class CountingEnumerable:IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return new CountingEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
class CountingEnumerator:IEnumerator<int>
{
int current =-1;
public bool MoveNext()
{
current++;
return current<10;
}
public int Current{get{return current;}}
object IEnumerator.Current{get{return Current;}}
public void Reset()
{
current =-1;
}
public void Dispose(){}
}
...
CountingEnumerable counter = new CountingEnumerable();
foreach(int x in counter)
{
Console.WriteLine(x);
}

3.4.4 Reflection and generics

USING TYPEOF WITH GENERIC TYPES

listing 3.11

1
2
3
4
5
6
7
8
9
10
11
12
static void DemonstrateTypeof<X>()
{
Console.WriteLine(typeof(X));
Console.WriteLine(typeof(List<>));
Console.WriteLine(typeof(Dictionary<,>));
Console.WriteLine(typeof(List<X>));
Console.WriteLine(typeof(Dictionary<string,X>));
Console.WriteLine(typeof(List<long>));
Console.WriteLine(typeof(Dictionary<long,Guid>));
}
...
DemonstrateTypeof<int>();

listing 3.12

1
2
3
4
5
6
7
8
9
10
11
12
string listTypeName="System.Collections.Generic.List`1";
Type defByName =Type.GetType(listTypeName);
Type closedByName = Type.GetType(listTypeName+"[System.String]");
Type closedByMethod = defByName.MakeGenericType(typeof(string));
Type closedByTypeof = typeof(List<string>);
Console.WriteLine (closedByMethod == closedByName);
Console.WriteLine(closedByName==closedByTypeof);

Type defByTypeof= typeof(List<>);
Type defByMethod = closedByName.GetGenericTypeDefinition();
Console.WriteLine(defByMethod==defByName);
Console.WriteLine(defByName==defByTypeof);

System.Type的属性和方法

有两个方法是最重要的GetGenericTypeDefinition和MakeGenericType(作用于泛型类型定义,返回一个已构造类型)

  1. 反射泛型方法

通过反射来调用泛型方法

Listing 3.13

1
2
3
4
5
6
7
8
9
public static void PrintTypeParameter<T>()
{
Console.WriteLine(typeof(T));
}
...
Type type = typeof(Snippet);
MethodInfo definition = type.GetMethod("PrintTypeParameter");
MethodInfo constructed = definition.MakeGenericMethod(typeof(string));
constructed.Invoke(null,null);

3.5 Limitations of generics in C# and other languages

3.5.1 Lack of generic variance

泛型是不变体

  1. 协变性在什么时候有用
  2. 没理解

####3.5.4 comparison with C++ templates

4 Saying nothing with nullable types

4.1 What do you do when you just dont have a value?

4.1.1 Why value type variables cant be null

4.1.2 Patterns for representing null values in C#1

4.2 System.Nullable <T>and System.Nullable

4.2.1 Introduciong Nullable<T>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void Display(Nullable<int> x)
{
Console.WriteLine("HasValue: {0}",x.HasValue);
if (x.HasValue)
{
Console.WriteLine("Value: {0}",x.Value);
Console.WriteLine("Explicit conversion: {0} ",(int)x);
}
Console.WriteLine("GetValueOrDefault(): {0}",x.GetValueOrDefault());
Console.WriteLine("GetValueOrDefault(10) : {0}",x.GetValueOrDefault(10));
Console.WriteLine("ToString(*): \"{0}\"",x.ToString());
Console.WriteLine("GetHashCode(): {0} ",x.GetHashCode());
Console.WriteLine();
}
...
Nullable<int> x = 5;
x = new Nullable<int>(5);
Console.WriteLine("Instance with value: ");
Display(x);
x= new Nullable<int>();
Console.WriteLine("Instance without value:");
Display(x);

4.2.2 Boxing Nullable<T> and unboxing

It is important to remember that Nullable<T> is a struct —a value type.

4.2.3 Equality of Nullable<T> instances

4.2.4 Support from the nongeneric Nullable class

4.3 C# 2’s syntactic sugar for nullable types

4.3.1 The ? modifier

list4.2 same as 4.1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int? nullable = 5;
object boxed = nullable;
Console.WriteLine(boxed.GetType());

int normal = (int)boxed;
Console.WriteLine(normal);

nullable = new int?();
boxed = nullable;

Console.WriteLine(boxed==null);

nullable = (int?)boxed;
Console.WriteLine(nullable.HasValue);

强调一波 代码最终是要给人读的 所以可读性和规范化很重要

We’ll model a Person class where you need to know a person’s name,date of birth,and date of death.Some of those people may still be alive—in which case the date of death will be represented by null.Here is some code .

Listing 4.4 Part of a Person class including calculation of age

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public TimeSpan get_Age()
{
if (!this.death.HasValue)
{
return (TimeSpan) (DateTime.Now - this.birth);
}
return (this.death.Value - this.birth);
}
//这是IL
public class Person
{
private DateTime birth;
private DateTime? death;
private string name;
public TimeSpan Age
{
get
{
if (death == null)
{
return DateTime.Now - birth;
}
else
{
return death.Value - birth;
}
}
}
public Person(string name, DateTime birth, DateTime? death)
{
this.birth = birth;
this.death = death;
this.name = name;
}
}
...
Person turing = new Person("Alan Turing", new DateTime(1912, 6, 23), new DateTime(1954, 4, 7));
Person knuth = new Person("Donald Knuth ", new DateTime(1938, 1, 10), null);
Console.WriteLine(turing.Age);
Console.WriteLine(knuth.Age);

4.3.2 Assigning and comparing with null

4.3.3 Nullable conversions and operators

可空类型也是可以转换的
1
2
3
4
5
int i=5;
if(i==null)
{
Console.WriteLine("Never going to happen");
}

这玩意会发出警告,因为两者都存在一个到int?的隐式转换

4.3.4 Nullable logic

4.3.5 Using the as operator with nullable types

1
2
3
4
5
6
7
8
9
static void PrintValueAsInt32(object o)
{
int? nullable = o as int?;
System.Console.WriteLine(nullable.HasValue? nullable.Value.ToString():"null");
}
...
System.Console.WriteLine("null as:");
PrintValueAsInt32(5);
PrintValueAsInt32("some string");

然而.net 4.5 以前is 和强制转换比 as快20倍

4.3.6 The null coalescing operator

空合并操作符

这个二元操作符在对first??second求值时:

  1. 对first进行求值
  2. 如果结果非空,则该结果就是整个表达式的结果
  3. 否则求second的值,其结果作为整个表达式的结果

之前的死亡日期属性

1
2
DateTime lastAlive = death??DateTime.Now
return lastAlive-birth;

涉及空值的时候就可以这么写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Address contact = user.COntractAddress;
if(contact ==null)
{
contact = order.ShippingAddress;
if(contact ==null)
{
contact = user.BillingAddress;
}
}


===>
Address contact = user.ContactAddress??
order.ShippingAddress??
user.BillingAddress;

4.4 Novel uses of nullable types

4.4.1 Tyeing an operation without using output parameter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static int? TryParse(string text)
{
int ret;
if (int.TryParse(text, out ret))
{
return ret;
}
else
{
return null;
}
}
...
int? parsed = TryParse("No valid");//(可空调用)
if (parsed != null)
{
System.Console.WriteLine("Parsed to {0}", parsed.Value);
}
else
{
System.Console.WriteLine("Countnt parse");
}

后面的方法更为条理清晰

####4.4.2 Painless comparisons with the null coalescing operator

1
2
3
4
5
public int Compare(Product first, Product second)
{
return PC.ReferenceCompare(first, second) ?? PC.Compare(second.Popularity, first.Popularity)
?? PC.Compare(first.Price, second.Price) ?? PC.Compare(first.Name, second.Name) ?? 0;
}

4.5 Summary

Nullable types solves a specific problem that only has somewhat ugly solutions before C#2.The delegate enhancements in C#2 act as a bridge between the familiarity of C#1 and the style of idiomatic C#3,which can often be raidcally different from earlier versions.

5 Fast-tracked delegates

5.1 Saying goodbye to awkward delegate syntax

list5.1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void LogPlainEvent(object sender, EventArgs e)
{
Console.WriteLine("LogPlain");
}

static void LogKeyEvent(object sender, KeyPressEventArgs e)
{
Console.WriteLine("LogKey");
}

static void LogMouseEvent(object sender, MouseEventArgs e)
{
Console.WriteLine("LogMouse");
}
...
Button button = new Button();
button.Text = "Click me";
button.Click += new EventHandler(LogPlainEvent);
button.KeyPress += new KeyPressEventHandler(LogKeyEvent);
button.MouseClick += new MouseEventHandler(LogMouseEvent);

Form form = new Form();
form.AutoSize = true;
form.Controls.Add(button);
Application.Run(form);

5.2 Method group conversions

5.3 Covariance and contravariance

list5.2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
Button button = new Button();
button.Text = "Click me";
button.Click += LogPlainEvent;
button.KeyPress += LogPlainEvent;
button.MouseClick += LogPlainEvent;

Form form = new Form();
form.AutoSize = true;
form.Controls.Add(button);
Application.Run(form);
static void LogPlainEvent(object sender, EventArgs e)
{
Console.WriteLine("LogPlain");
}

5.3.1 Contravariance for delegate parameters

5.3.2 Covariance of delegate return types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
delegate Stream StreamFactory();
static MemoryStream GenerateSampleData()
{
byte[] buffer = new byte[16];
for(int i=0;i<buffer.Length;i++)
{
buffer[i]=(byte)i;
}
return new MemoryStream(buffer);
}
...
StreamFactory factory = GenerateSampleData;
using(Stream stream = factory())
{
int data;
while((data=stream.ReadByte())!=-1)
{
Console.WriteLine(data);
}
}

5.3.3 A small risk of incompatibility

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
delegate void SampleDelegate(string x);

public void CandidateAction(string x)
{
Console.WriteLine("Snippet.CandidateAction");
}

public class Derived : Snippet
{
public void CandidateAction(object o)
{
Console.WriteLine("Derived.CandidateAction");
}
}

...
Derived x = new Derived();
SampleDelegate factory = new SampleDelegate(x.CandidateAction);
factory("test");

5.4 Inline delegate actions with anonymous methods

5.4.1 Starting simply : acting on a parameter

list5.5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Action<string> printReverse = delegate(string text)
{
char[] chars = text.ToCharArray();
Array.Reverse(chars);
Console.WriteLine(new string(chars));
};
Action<int> printRoot = delegate(int number) { Console.WriteLine(Math.Sqrt(number)); };
Action<IList<double>> printMean = delegate(IList<double> numbers)
{
double total = 0;
foreach (double value in numbers)
{
total += value;
}
Console.WriteLine(total / numbers.Count);
};
printReverse("Hello her");
printRoot(2);
printMean(new double[] { 1.5, 2.5, 3, 4.5 });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//三级跳
delegate int calculator(int x, int y); //委托类型
static void Main()
{
calculator cal = new calculator(Adding);
int He = cal(1, 1);
Console.Write(He);
}

public static int Adding(int x, int y)
{
return x + y;
}

delegate int calculator(int x, int y); //委托 匿名方法 其实就是省略了 委托的方法名称
static void Main()
{
calculator cal = delegate(int num1,int num2)
{
return num1 + num2;
};
int he = cal(1, 1);
Console.Write(he);
}

delegate int calculator(int x, int y); //委托类型
static void Main()
{
calculator cal = (x, y) => x + y;//Lambda表达式,大家发现没有,代码一个比一个简洁
int he = cal(1, 1);
Console.Write(he);
}

5.4.2 Returing values from anonymous methods

list 5.7

1
2
Predicate<int> isEven = delegate(int x) { return x % 2 == 0; };
Console.WriteLine(isEven(1));

​ 新的语法和我们所期望的几乎完全相符__不需要在delegate那里声明返回类型.The compiler checks that all the possible return values are compatible with the declared return type of the delgate type it is trying to convert the anonymous method into.

list5.8

1
2
3
4
5
6
7
8
9
10
11
SortAndShowFiles(
"Sorted by name:",
delegate(FileInfo f1, FileInfo f2) { return f1.Name.CompareTo(f2.Name); });
SortAndShowFiles(
"Sorted by length: ",
delegate(FileInfo f1, FileInfo f2) { return f1.Length.CompareTo(f2.Length); });
SortAndShowFiles("Sorted by name lambda: ",
(x, y) =>
{
return x.Name.CompareTo(y.Name);
});

5.4.3 Ignoring delegate parameters

list 5.6 代码精简

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<int> x = new List<int>();
x.Add(5);
x.Add(10);
x.Add(15);
x.Add(20);
x.Add(25);
x.ForEach(
delegate(int n)
{
Console.WriteLine(Math.Sqrt(n));
}
);
x.ForEach(
(n) =>
{
Console.WriteLine(Math.Sqrt(n));
});

list 5.9 Subcribing to events with anonymous methods that ignore parameters

1
2
3
4
5
6
7
8
9
10
11
Button button = new Button();
button.Text = "Click me";
button.Click += delegate { Console.WriteLine("LogPlain"); };
button.KeyPress += delegate { Console.WriteLine("LogKey"); };
button.MouseClick += delegate { Console.WriteLine("LogMouse"); };
Form form = new Form();
form.AutoSize = true;
form.Controls.Add(button);
Application.Run(form);

how about lambda

5.5 Capturing variables in anonymous methods

list 5.10 Examples of variable kinds with respect to anonymous methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void EnclosingMethod()
{
int outerVariable = 5;
string capturedVariable = "captured";
if (DateTime.Now.Hour == 9)
{
int normallLocalVariable = DateTime.Now.Minute;
Console.WriteLine(normallLocalVariable);
}
MethodInvoker x = delegate()
{
string anonLocal = "local to anonymous method";
Console.WriteLine(capturedVariable+anonLocal);
};
x();
}

5.5.1 Defining closures and different types of variables

5.5.2 Examining the behavior of captured variables

1
2
3
4
5
6
7
8
9
10
11
string captured = "Before x is captured";
MethodInvoker x = delegate
{
Console.WriteLine(captured);
captured = "Changed by x";
};
captured = "directly before x is invoked";
x();
Console.WriteLine(captured);
captured = "before second invocation";
x();
1
2
3
4
5
6
7
List<Person> FindAllYoungerThan(List<Person> people,int limit)
{
return people.FindAll(delegate (Person person)
{
return person.Age<limit;
});
}

5.5.3 What’s the point of captured variables

5.5.4 The extended lifetime of captured variables

list5.12 Demonstration of a captured variabled variable having its lifetime extended

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Main(string[] args)
{
MethodInvoker x = CreateDelegateInstance();
x();
x();
}

static MethodInvoker CreateDelegateInstance()
{
int counter = 5;
MethodInvoker ret = delegate
{
Console.WriteLine(counter);
counter++;
};
ret();
return ret;
}

A captured varibale lives for at least as long as any delegate instance referring to it.

我们一般会人为counter在栈上,实际上在堆上?

####5.5.5 Local vairable instantiations

比较这两段代码

1
2
3
4
5
6
7
8
9
10
11
12
int single;
for(int i =0;i<10;i++){
single =5;
cw(single+i);
}

//latter
for(int i =0;i<10;i++)
{
int multiple =5;
Cw(multiple+i);
}

后者被创建了10次moultiple

list5.13 capturing multiple variable instantiations with multiple delegates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
List<MethodInvoker> list = new List<MethodInvoker>();
for (int index = 0; index < 5; index++)
{
int counter = index * 10; //实例化counter 多次 每次都是不同的变量
list.Add(
delegate
{
Console.WriteLine(counter);//打印并递增捕获的变量
counter++;
});
}
foreach (MethodInvoker t in list)
{
t(); //执行全部5个委托
}
list[0]();
list[0]();
list[0]();
list[1]();

####5.5.6 Mixtures of shared and distinct variables

list 5.14 Capturing variables in different scopes. Warning : nasty code ahead!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MethodInvoker[] delegates = new MethodInvoker[2];
int outside = 0; //Instatiates variable once
for (int i = 0; i < 2; i++)
{
int inside = 0; //Instatiates variable multiple times
delegates[i] = delegate //Captures variables with anonymous method
{
Console.WriteLine("({0},{1})", outside, inside);
outside++;
inside++;
};
}
MethodInvoker first = delegates[0];
MethodInvoker second = delegates[1];
first();
first();
first();
second();
second();

####5.5.7 Captured variable guidelines and summary

  • If Code that doesnot use captured variables is just as simple as code that does,dont use them
  • Before capturing a variable declared by a for or foreach statement,consider whether your delegate is going to live beyont the loop iteration, and whether your want it to see the subsequent values of that variable. If not ,create another variable inside the loop that just copies the value you do want
  • If you create multiple delegate instances that capture variables, put thought into whether your want them to capture the same variable
  • If you capture avariable that doesnt actually change,you dont need to worry as much
  • If the delegate instances your create never escape from the method - in other words,they’re never stored anywhere else,or returned, or used for starting threads-life is a lot simple
  • Consider the extended lifetie of any captured variables in terms of garbage collection. This is normally not an issue,but if you capture an object that’s expensive in terms of memory, it may be significant’

    5.6 Summary

  • The varibale is captured - not its value at the point of delegate instance creation
  • Captured variables have lifetimes extended to at least that of the capturing delegate
  • Multiple delegates can capture the same variable…
  • …but within loops,the same variable declaration can effectively refer to different variable “instances”
  • for loop declarations careate variable that live for the duration of the loop– they are not instantiated on each iteration. the same is true for foreach statements before C#5
  • Extra types are created ,where necessary,to hold captured variable.
  • Be careful! Simple is almost always better than clever.
    1
    2
    3
    4
    5
    木兰花令·拟古决绝词
    人生若只如初见,何事秋风悲画扇。
    等闲变却故人心,却道故人心易变。
    骊山语罢清宵半,泪雨零铃终不怨。
    何如薄幸锦衣郎,比翼连枝当日愿。

6 Inplementing iterators the easy way

6.1 C#1 The pain of hadwritten iterators

listing 6.1 Code using the (as yet unimplemented) new collection type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Program
{
static void Main(string[] args)
{
object[] values = { "a", "b", "c", "d", "e" };
IterationSample collection = new IterationSample(values, 3);
foreach (object x in collection)
{
Console.WriteLine(x);
}
}
}

public class IterationSample : IEnumerable
{
public object[] values;
public int startingPoint;

public IterationSample(object[] values, int startingPoint)
{
this.values = values;
this.startingPoint = startingPoint;
}

public IEnumerator GetEnumerator()
{
return new IterationSampleIterator(this);
}
}

public class IterationSampleIterator : IEnumerator
{
IterationSample parent;
int position;

internal IterationSampleIterator(IterationSample parent)
{
this.parent = parent;
position = -1;
}

public bool MoveNext()
{
if (position != parent.values.Length)
{
position++;
}
return position < parent.values.Length;
}

public object Current
{
get
{
if (position == -1 || position == parent.values.Length)
{
throw new InvalidOperationException();
}
int index = position + parent.startingPoint;
index = index % parent.values.Length;
return parent.values[index];
}
}

public void Reset()
{
position = -1;
}
}

6.2 C#2 Simple iterators with yield statements

listing 6.4 Iterating through the sample collection with c#2 and yield return

1
2
3
4
5
6
7
8
public IEnumerator GetEnumerator()
{
for (int index = 0; index < values.Length; index++)
{
yield return values[(index + startingPoint) % values.Length];
}
}
//That replaces the whole of the IterationSampleIterator class

6.2.1 Introducing iterator blocks and yield return

6.2.2 Visualizing an iterator workflow

listing 6.6 showing the sequence of calls between an iterator and its caller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private static readonly string Padding = new string(' ', 30);

static IEnumerable<int> CreateEnumerable()
{
Console.WriteLine("{0} Start of CreateEnumerable()",Padding);
for (int i = 0; i < 3; i++)
{
Console.WriteLine("{0} About to yield {1} ",Padding,i);
yield return i;
Console.WriteLine("{0} After yield ",Padding);
}
Console.WriteLine("{0}Yielding final value",Padding);
yield return -1;
Console.WriteLine("{0} End of CreateEnumerable()",Padding);
}

...
static void Main(string[] args)
{
IEnumerable<int> iterable = CreateEnumerable();
IEnumerator<int> iterator = iterable.GetEnumerator();
Console.WriteLine("starting to iterate");
while (true)
{
Console.WriteLine("Calling Move NExt()...");
bool result = iterator.MoveNext();
Console.WriteLine("...MoveNext result={0}",result);
if (!result)
{
break;
}
Console.WriteLine("Fetching Current...");
Console.WriteLine("...Current result={0}",iterator.Current);
}
}
  • None of the code in CreateEnumerable is called until the first call to MoveNext.
  • It is only when you call MoveNext that any real work gets done.Fetching Current doesn’t run any of your code
  • The code stops executing at yield return and picks up again just afterward at the next call to MoveNext
  • You can have multiple yield return statements in different places in the method
  • The code doesn’t end at the last yield return .Instead, the call to MoveNext that causes you toreach the end of the method is the one that returns false.

6.2.3 Advanced iterator execution flow

listing6.6 Demonstration of yield break

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static IEnumerable<int> CountWithTimeLimit(DateTime limit)
{
for (int i = 1; i < 100; i++)
{
if (DateTime.Now > limit)
{
yield break;
}
yield return i;
}
}
...
DateTime stop = DateTime.Now.AddSeconds(2);
foreach (var i in CountWithTimeLimit(stop))
{
Console.WriteLine("Received {0}",i);
Thread.Sleep(300);
}

listing6.7 Demonstration of yield break working with try/finally

1
2


6.2.4 Quirks in the implementation

6.3 Real-lifer iterator examples

6.3.1 Iteration over the dates in a timetable

6.3.2 Iterating over lines in a file

listing 6.8 Looping over the lines in a file using an iterator blcok

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static IEnumerable<string> ReadLines(string filename)
{
using (TextReader reader = File.OpenText(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
...
foreach (var line in ReadLines("test.txt"))
{
Console.WriteLine(line);
}

6.3.3 Filtering items lazily using an iterator block and a predicate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static IEnumerable<T> Where<T>(IEnumerable<T> source, Predicate<T> predicate)
{
if (source == null || predicate == null)
{
throw new ArgumentNullException(); //Eagerly checks arguments
}
return WhereImpl(source, predicate); //lazily processes data
}

public static IEnumerable<T> WhereImpl<T>(IEnumerable<T> source, Predicate<T> predicate)
{
foreach (var item in source)
{
if (predicate(item)) //Tests current item against predicate
{
yield return item;
}
}
}
static IEnumerable<string> ReadLines(string filename)
{
using (TextReader reader = File.OpenText(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
...
IEnumerable<string> lines = ReadLines("../../FakeLinq.cs");
Predicate<string> predicate = ((x) => { return x.StartsWith("using"); });
foreach (var item in Where(lines,predicate))
{
Console.WriteLine(item);
}

我们将实现分为两部分: 参数验证和真正的过滤逻辑.假设将所有内容放在同一个方法里,那么调用Where<string>(null,null)时什么都不会发生

6.4 Pseudo-synchronous code with the Concurrency and Coordination Runtime

skip but worth Reading

6.5 Summary

7 Concluding C#2 : the final features

This chapter covers

  • Partial types
  • Static classes
  • Separate getter/setter property access
  • Namespace aliases
  • Pragma directives
  • Fixed-size buffers
  • Friend assemblies

7.1 Patial types

7.2 Static classes

工具类:

  • 所有的成员都是静态的(除了私有构造函数)
  • 类直接从object派生
  • 一般情况下不应该有状态,除非涉及高速缓存或单例
  • 不能存在任何可见的构造函数
  • 类可以是密封的(添加sealed修饰符

Listing 7.2 A partial method called from a constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
partial class PartialMethodDemo
{
public PartialMethodDemo()
{
OnConstructorStart();
Console.WriteLine("Generated constructor");
OnConstructorEnd();
}

partial void OnConstructorStart();

partial void OnConstructorEnd();
}

partial class PartialMethodDemo
{
partial void OnConstructorEnd()
{
Console.WriteLine("Manual code");
}
}

listing 7.3 sealed class NonStarticStringHelper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public sealed class NonStaticsStringHelper  // seals class to prevent derivation
{
private NonStaticsStringHelper() // Prevents instantiation from other code
{

}

public static string Reverse(string input) // All methods are static
{
char[] chars = input.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}

listing 7. 4 The same utility class as in listing 7.3 but converted into a c#2 static class

1
2
3
4
5
6
7
8
9
public static class StringHelper
{
public static string Reverse(string input)
{
char[] chars = input.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}

7.3 Separate getter/setter property access

listing

1
2
3
4
5
6
7
8
9
10
private string name;

public string Name
{
get { return name; }
private set
{
name = value;
}
}

7.4 Namespace class

在C#2中,有三种别名种类: C#1 的命名空间别名/ 全局命名空间别名和外部别名.

listing 7.5 Using aliases to distinguish between different button types

1
2
3
4
5
6
7
8
using WinForms = System.Windows.Forms;
using WebForms = System.Web.UI.WebControls;

static void Main(string[] args)
{
Console.WriteLine(typeof(WinForms.Button));
Console.WriteLine(typeof(WinForms.Button));
}

listing 7.6 use : : to tell the compiler to use aliases

1
2
3
4
5
6
7
8
9
10
11
using WinForms = System.Windows.Forms;
using WebForms = System.Web.UI.WebControls;
class WinForms { }
class Program
{
static void Main(string[] args)
{
Console.WriteLine(typeof(WinForms::Button));
Console.WriteLine(typeof(WinForms::Button));
}
}

7.4.2 The global namespace alias

listing 7.7 Use of the global namespace alias to specify the desired type exactly

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Configuration { }

namespace Learn
{
class Configuration { }
class WriteAny
{
private static void Main()
{
Console.WriteLine(typeof(Configuration));
Console.WriteLine(typeof(global::Configuration));
Console.WriteLine(typeof(global::Learn.WriteAny));
}
}
}

7.4.3 Extern aliases

Mark!

listing 7.8 Working with different types of the same type in different assemblies

1
2
3
4
5
6
7
8
9
extern alias FirstAlias;
extern alias SecondAlias;
using FD = FirstAlias::Demo;

static void Main(string[] args)
{
Console.WriteLine(typeof(FD.Example));
Console.WriteLine(typeof(SecondAlias::Demo.Example));
}

7.5 Pragma directives

7.5.1 Warning pragmas

有时候你需要忽略一个警告

listing 7.9 Class containing an unused field

1
2
3
4
5
6
public class FieldUsedOnlyByReflection
{
#pragma warning disable 169
private int x;
#pragma warning restore 169
}

7.5.2 Checksum pragmas

Skip

7.6 Fixed-size buffers in unsafe code

UnSafe

fixed byte data[20]

listing 7.11 Demonstration of fixed-size buffers to obtain console color information

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
namespace Fixedsizebuffers
{
using System;
using System.Runtime.InteropServices;

struct COORD
{
public short X, Y;
}

struct SMALL_RECT
{
public short Left, Top, Right, Bottom;
}

unsafe struct CONSOLE_SCREEN_BUFFER_INFOEX
{
public int StructureSize;
public COORD ConsoleSize, CursorPosition;

public short Attributes;

public SMALL_RECT DisplayWindow;
public COORD MaximumWindowSize;

public int FullScreenSupported;

public fixed int ColorTable[16];
}


static class FixedSizeBufferDemo
{
private const int StdOutputHandle = -11;

[DllImport("kernel32.dll")]
static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32.dll")]
static extern bool GetConsoleScreenBufferInfoEx(IntPtr handle, ref CONSOLE_SCREEN_BUFFER_INFOEX info);
unsafe static void Main(string[] args)
{
IntPtr handle = GetStdHandle(StdOutputHandle);
CONSOLE_SCREEN_BUFFER_INFOEX info;
info = new CONSOLE_SCREEN_BUFFER_INFOEX();
info.StructureSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
GetConsoleScreenBufferInfoEx(handle, ref info);
for (int i = 0; i < 16; i++)
{
Console.WriteLine("{0:x6}",info.ColorTable[i]);
}
}
}
}

7.7 Exposing internal members to selected assemblies

Listing 7.11 Demonstration of fixed-size bufer to obtain console color information

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Compiled to Source.dll
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("FriendAssembly")] //授予额外的访问权限

public class Source
{
internal static void InternalMethod() { }
public static void PublicMethod() { }
}

//COmpiled to FriendAssembly.dll
public class Friend
{
static void Main()
{
Source.InternalMethod(); //在FriendAssembly中使用额外的访问权限
Source.PublicMethod();
}
}

//Compiled to EnemyAssembly.dll
public class Enemy
{
static void Main()
{
//Source.InternalMethod();
Source.PublicMethod(); //EnemyAssembly不具备特殊的访问权限 依旧可以正常访问公有方法.
}
}

7.7.1 Friend assemblies in the simple selected assemblies

7.7.2 Why use InternalsVisibleTo

7.7.3 InternalsVisibleTO and signed assemblies

7.8 Summary

总结 一句 按需查找

8 Cutting fluff with a smart compiler

  • Automatically implemented properties
  • Implicitly typed local variables
  • Object and collection initializers
  • Implicitly typed arrays
  • Anonymous types

8.1 Automatically implemented properties

安全但是没有什么卵用的静态自动属性

listing8.1 Counting instances awkwardly with a static automatic property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Person
{
public string Name { get; private set; } //Declares properties with public getters
public int Age { get; private set; }
private static int InstanceCounter { get; set; } //Declares private static property and lock

private static readonly object counterLock = new object();

public InstanceCountingPerson(string name, int age)
{
Name = name;
Age = age;
lock (counterLock) //Uses lock for safe property access
{
InstanceCounter++;
}
}
}

8.2 Implicit typing of local variables

var variableName = someInitialValue;

此处为推断类型 而不是动态类型

8.2.1 Using var to declare a local variable

8.2.2 Restrictions on implicit typing

使用隐式类型的限制

  • 被声明的变量是一个局部变量,而不是静态字段和实例字段
  • 变量在声明的同时被初始化
  • 初始化表达式不是方法组,也不是匿名函数
  • 初始化表达式不是null
  • 语句中只声明了一个变量
  • 你希望变量拥有的类型是初始化表达式的编译时类型
  • 初始化表达式不包含正在声明的变量

用方法调用的结果来初始化一个变量可能是隐式类型最常用的应用

8.2.3 Pros and cons of implicit typing

主要是为了可读性

  • 如果让读代码的人以沿就能看出变量的类型很重要 ,就用显式类型
  • 如果变量直接用一个构造函数初始化,并且类型名很长,就考虑使用隐式类型.
  • 如果变量的确切类型不重要,就用隐式类型
  • 在开发新项目的时候,与团队成员进行商议
  • 如有疑虑,两种方式都写写,用舒服的

8.2.4 Recommendations

8.3 Simplified initialization

8.3.1 Defining some sample types

listing8.2 A fairly simple Person class used for further demonstrations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Person
{
public string Name { get; private set; }
public int Age { get; private set; }

private List<Person> friends = new List<Person>();
public List<Person> Friends {
get
{
return friends;
}
}

private Location home = new Location();
public Location Home { get { return home; } }
public Person() { }

public Person(string name)
{
Name = name;
}
}
public class Location
{
public string Country { get; set; }
public string Town { get; set; }
}

8.3.2 Setting simple properties

1
2
3
4
5
Person tom = new Person("Tom")
{
Age = 9;
Home = {Country ="UK", Town="Reading"}
};

8.3.3 Setting properties on embedded objects

8.3.4 Collection initializers

1
2
var names = new List<string> { "Holly", "Jon", "Tom", "Robin", "William" };
var nameAgeMap = new Dictionary<string, int> { { "Holly", 35 }, { "Jon", 26 }, { "Tom", 9 } };

listing 8.3 Building up a rich object using object and collection initializers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Person tom = new Person
{
Name ="Tom",
Age=9,
Home = {Town ="Reading",Country ="UK"},
Friends=
{
new Person{Name = "Alberto"},
new Person("Max"),
new Person{Name ="Zak",Age=7},
new Person("Ben"),
new Person("Alice")
{
Age=9,
Home={Town="Twyford",Country="UK"}
}
}
};

8.3.5 Uses of initialization features

8.4 Implicitly typed arrays

MyMethod({"Holly","Jon","Tom","Robin","William"})

8.5 Anonymous types

Listing 8.4 Creating objects of an anonymous type with Name and Age properties

1
2
3
4
var tom = new {Name ="Tom",Age=9};
var holly = new {Name = "Holly",Age=36};
var jon = new {Name = "Jon",Age=36};
System.Console.WriteLine("{0} is {1} years old",jon.Name,jon.Age);

8.5.1 First encounters of the anonymous kind

Listing 8.5 Populatio an array using anonymous types and then finding the total age

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var family = new []
{
new { Name ="Holly", Age = 36 },
new { Name ="Jon", Age =36 },
new { Name ="Tom", Age = 9 },
new { Name = "Robin", Age = 6 },
new { Name = "William" , Age = 6 }
};
int totalAge = 0;
foreach (var person in family)
{
totalAge +=person.Age;
}
System.Console.WriteLine("Total Age : {0}",totalAge);

8.5.2 members of anonymous types

8.5.3 Projection initializers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
List<Person> family = new List<Person>                 
{
new Person { Name="Holly", Age=37 },
new Person { Name="Jon", Age=36 },
new Person { Name="Tom", Age=9 },
new Person { Name="Robin", Age=6 },
new Person { Name="William", Age=6 }
};

var converted = family.ConvertAll(delegate(Person person)
{ return new { person.Name, IsAdult = (person.Age >= 18) }; }
);

foreach (var person in converted)
{
Console.WriteLine("{0} is an adult? {1}",
person.Name, person.IsAdult);
}

8.5.4 What’s the point?

8.6 Summary

9 Lambda expressions and expression trees

9.1.2 First transformatio to a lambda expression

list 9.1 Using an anonymous method to create a delegate instance

1
2
3
Func<string,int> returnLength;
returnLength = delegate(string text){return text.Length;};
Console.WriteLine(returnLength("Hello"));

list 9.2 A long-winded first lambda expression,similar to an anonymous method

1
2
3
Func<string,int> returnLength;
returnLength = (string text)=>{return text.Length;};
Console.WriteLine(returnLength("Hello"));

9.2 Simple examples using List<T> and events

9.2.1 Filtering ,sorting,and actions on lists

listing 9.4 Manipulation a list of films using lambda expressions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Film
{
public string Name { get; set; }
public int Year { get; set; }
}

...
var films = new List<Film>
{
new Film {Name="Jaws", Year=1975},
new Film {Name="Singing in the Rain", Year=1952},
new Film {Name="Some like it Hot", Year=1959},
new Film {Name="The Wizard of Oz", Year=1939},
new Film {Name="It's a Wonderful Life", Year=1946},
new Film {Name="American Beauty", Year=1999},
new Film {Name="High Fidelity", Year=2000},
new Film {Name="The Usual Suspects", Year=1995}
};

Action<Film> print =
film => Console.WriteLine("Name={0}, Year={1}", film.Name, film.Year);

// Note: extra lines added for clarity when running
Console.WriteLine("All films");
films.ForEach(print);
Console.WriteLine();

Console.WriteLine("Oldies");
films.FindAll(film => film.Year < 1960)
.ForEach(print);
Console.WriteLine();

Console.WriteLine("Sorted");
films.Sort((f1, f2) => f1.Name.CompareTo(f2.Name));
films.ForEach(print);

9.2.2 Logging in an event handler

Listing9.5 Logging events using lambda expressions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void Log(string title, object sender, EventArgs e)
{
Console.WriteLine("Event: {0}", title);
Console.WriteLine(" Sender: {0}", sender);
Console.WriteLine(" Arguments: {0}", e.GetType());
foreach (PropertyDescriptor prop in
TypeDescriptor.GetProperties(e))
{
string name = prop.DisplayName;
object value = prop.GetValue(e);
Console.WriteLine(" {0}={1}", name, value);
}
}

...
Button button = new Button();
button.Text = "Click me";
button.Click += (src, e) => Log("Click", src, e);
button.KeyPress += (src, e) => Log("KeyPress", src, e);
button.MouseClick += (src, e) => Log("MouseClick", src, e);

Form form = new Form();
form.AutoSize = true;
form.Controls.Add(button);
Application.Run(form);

9.3 Expression trees

9.3.1 Building expressiono trees programmatically

listing 9.6 A simple expression tree ,adding 2 and 3;

1
2
3
4
Expression firstArg = Expression.Constant(2);
Expression secondArg = Expression.Constant(3);
Expression add = Expression.Add(firstArg, secondArg);
Console.WriteLine(add);

9.3.2 Compiling expression trees into delegates

listing 9.7 Compiling and executing an expression tree

1
2
3
4
5
6
Expression firstArg = Expression.Constant(2);
Expression secondArg = Expression.Constant(3);
Expression add = Expression.Add(firstArg, secondArg);

Func<int> compiled = Expression.Lambda<Func<int>>(add).Compile();
Console.WriteLine(compiled());

9.3.3 COnverting C# lambda expressions to expression trees

listing 9.8 Compliing and executing an expression tree

1
2
3
Expression<Func<int>> return5 = () => 5;
Func<int> compiled = return5.Compile();
Console.WriteLine(compiled());

listing 9.9 Demonstration of a more complicated expression tree

1
2
3
4
Expression<Func<string, string, bool>> expression = (x, y) => x.StartsWith(y);
var compiled = expression.Compile();
Console.WriteLine(compiled("First","Second"));
Console.WriteLine(compiled("First","Fir"));

listing 9.10 Building a method call expression tree in code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var target = Expression.Parameter(typeof(string), "x");
var methodArg = Expression.Parameter(typeof(string), "y");
Expression[] methodArgs = new[] { methodArg };

Expression call = Expression.Call(target, method, methodArgs);

var lambdaParameters = new[] { target, methodArg };
var lambda = Expression.Lambda<Func<string, string, bool>>(call, lambdaParameters);

var compiled = lambda.Compile();

Console.WriteLine(compiled("First", "Second"));
Console.WriteLine(compiled("First", "Fir"));

9.3.4 Expression trees at the heart of LINQ

9.3.5 Expression trees beyond LINQ

9.4 Changes to type inference and overload resolution

9.4.1 Reasons for change: streamlining generic method calls

listing 9.11 Example of code requiring the new type inference rules

1
2
3
4
5
6
7
static void PrintConvertedValue<TInput,TOutput>
(TInput input,Converter<TInput,TOutput> converter)
{
Console.WriteLine(converter(input));
}
...
PrintConvertedValue("I am a string", x => x.Length);

9.4.2 Inferred return types of anonymous functions

listing 9.12 Attempting to infer the return type of an anonymous method

1
2
3
4
5
6
7
8
delegate T MyFunc<T>();
static void WriteResult<T>(MyFunc<T> function)
{
Console.WriteLine(function());
}

...
WriteResult(delegate { return 5; });

listing 9.13 Code returning an integer or an object depending on the time of day

1
2
3
4
5
6
delegate T MyFunc<T>();
static void WriteResult<T>(MyFunc<T> function)
{
Console.WriteLine(function());
}
...

9.4.3 Two-phase type inference

listing 9.14 Flexible type inference combining information from multiple arguments

1
2
3
4
5
6
static void PrintType<T>(T first, T second)
{
Console.WriteLine(typeof(T));
}
...
PrintType(1,new object());

listing 9.15 Multistage type inferenct

1
2
3
4
5
6
7
8
9
10
11
static void ConvertTwice<TInput, TMiddle, TOutput>(
TInput input,
Converter<TInput, TMiddle>firstConversion,
Converter<TMiddle, TOutput>secondConversion)
{
TMiddle middle = firstConversion(input);
TOutput output = secondConversion(middle);
Console.WriteLine(output);
}
...
ConvertTwice("Another string", text => text.Length, length => Math.Sqrt(length));

listing 9.16 Sample of overloading choice influenced by delgate return type

1
2
3
4
5
6
7
8
9
10
11
static void Execute(Func<int> action)
{
Console.WriteLine("action returns an int: " + action());
}

static void Execute(Func<double> action)
{
Console.WriteLine("action returns a double: " + action());
}
...
Execute(()=>1);

9.4.4 Picking the right overloaded method

9.4.5 Wrapping up tpe inference and overload resolution

9.5 Summary

10 Extension methods

This chapter covers

  • Writing extension methods
  • Calling extension methods
  • Method chaining
  • Extension methods in .net 3.5
  • Other uses for extension methods

10.1 Life before extension methods

listing 10.1 A simple utility class to provide extra functionality for streams

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class StreamUtil
{
private const int BufferSize = 8192;

public static void Copy(Stream input, Stream output)
{
byte[] buffer = new byte[BufferSize];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
}

public static byte[] ReadFully(Stream input)
{
using (MemoryStream tempStream = new MemoryStream())
{
{
Copy(input, tempStream);
return tempStream.ToArray();
}
}
}
}

listing 10.2 Using StreamUtil to copy a web response stream to a file

1
2
3
4
5
6
7
8
9
10
static void Main(string[] args)
{
WebRequest request = WebRequest.Create("http://www.baidu.com");
using (WebResponse response = request.GetResponse())
using (Stream responseStream = response.GetResponseStream())
using (FileStream output = File.Create("response.dat"))
{
StreamUtil.Copy(responseStream, output);
}
}

10.2 Extension method syntax

10.2.1 Declearing extension methods

listing 10.3 The StreamUtil class again,but this time with extension methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class StreamUtil
{
private const int BufferSize = 8192;

public static void Copy(Stream input, Stream output)
{
byte[] buffer = new byte[BufferSize];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
}

public static byte[] ReadFully(Stream input)
{
using (MemoryStream tempStream = new MemoryStream())
{
{
Copy(input, tempStream);
return tempStream.ToArray();
}
}
}
}

10.2.2 Calling extensio methods

listing 10.4 Copying a stream using an extension method

1
2
3
4
5
6
7
8
9
10
static void Method2()
{
WebRequest request = WebRequest.Create("http://www.baidu.com");
using (WebResponse response = request.GetResponse())
using (Stream responseStream = response.GetResponseStream())
using (FileStream output = File.Create("response.dat"))
{
responseStream.CopyTo(output);
}
}

10.2.3 Extension method discovery

10.2.4 Calling a method discovery

listing 10.5 Extension method being called on a null reference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class NullUtil
{
public static bool IsNull(this object x)
{
return x == null;
}
}

static void List5()
{
object y = null;
Console.WriteLine(y.IsNull());
y = new object();
Console.WriteLine(y.IsNull());
}

10.3 Extension methods in .NET 3.5

10.3.1 First steps with Enumerable

listing 10.6 Using Enumerable.Range to print out the numbers 0~9

1
2
3
4
5
6
...
var collection = Enumerable.Range(0, 10);
foreach (var c in collection)
{
Console.WriteLine(c);
}

listing 10.7 Reversing a collection with the Reverse method

1
2
3
4
5
6
7
...
var collection = Enumerable.Range(0, 10)
.Reverse();
foreach (var c in collection)
{
Console.WriteLine(c);
}

10.3.2 Filtering with WHere and chaining method calls together

listing 10.8 Using the where method with a lambda expression to find odd numbers

1
2
3
4
5
var collection = Enumerable.Range(0, 10).Where(x => x % 2 != 0).Reverse();
foreach (var c in collection)
{
Console.WriteLine(c);
}

10.3.3 Interlude : haven’t we seen the Where method before?

listing 10.9 Projection using a lambda expression and an anonymous type

1
2
3
4
5
6
7
8
9
var collection = Enumerable.Range(0, 10)
.Where(x => x % 2 != 0).Reverse()
.Select(x => new { Original = x, SquareRoot = Math.Sqrt(x) });
foreach (var element in collection)
{
Console.WriteLine("sqrt({0}) = {1}",
element.Original,
element.SquareRoot);
}

10.3.4 Projections using the select method and anonymous types

10.3.5 Sorting using the OrderBy method

listing 10.10 Ordering a sequence by two properties

1
2
3
4
5
6
7
8
var collection = Enumerable.Range(-5, 11)
.Select(x => new { Original = x, Square = x * x })
.OrderBy(x => x.Square)
.ThenBy(x => x.Original);
foreach (var c in collection)
{
Console.WriteLine(c);
}

10.3.6 Business examples involving chaining

1
2
3
4
5
6
7
company.Departments
.Select(dept => new
{
dept.Name,
Cost = dept.Employees.Sum(person => person.Salary)
})
.OrderByDescending(deptWithCost => deptWithCost.Cost);
1
2
3
bugs.GroupBy(bug => bug.AssignedTo)
.Select(list => new {Developer = list.Key,Count = list.Count() })
.OrderByDescending(x=>x.Count);

10.4 Usage ideas and guidelines

10.4.1 “Extending the world “ and making interfaces richer

10.4.2 Fluent interfaces

10.4.3 Using extension methods sensibly

10.5 Summary

LINQ

Query expressions and LINQ to Objects

This chapter covers

  • Streaming sequences of data and deferred execution
  • Standard query operators and query expression
  • Range variables and transparent identifiers
  • Projecting,Filtering , and sorting
  • Joining and grouping
  • Choosing which syntas to use

11.1 Introducing LINQ

延迟执行 和流处理

Reverse 成为一个缓冲操作

listing 11.1 Trivial query to print the list of users

1
2
var query = from user in SampleData.AllUsers
select user;

listing 11.2 The query expression of listing 11.1 translated into a method call

1
var query = SampleData.AllUsers.Select(user=>user);

listing 11.3 Compilier translation calling methods on a dummy LINQ implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class TranslationExample{
static void Main()
{
var source = new Dummy<string>();
var query = from dummy in source
where dummy.ToString() == "Ignored"
select "Anything";
}
}

static class Extensiions
{
public static Dummy<T> Where<T>(this Dummy<T> dummy,Func<T,bool> predicate)
{
Console.WriteLine("Where called");
return dummy;
}
}

class Dummy<T>
{
public Dummy<U> Select<U>(Func<T,U> selector)
{
Console.WriteLine("Select called");
return new Dummy<U>();
}
}

//output
Where called
Select called
//equals
var query = source.Where(dummy => dummy.ToString() == "Ignored")
.Select(dummy => "Anything")

listing 11.4 Query selecting just the names of the users

1
2
3
IEnumerable<string> query = from user in SampleData.AllUsers select user.Name;
//translation
SampleData.AllUsers.Select(user =>user.Name)

11.2 Simple beginnings : selecting elements

listing11.5 using Cast and OfType to work with weakly typed collections

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void List115()
{
ArrayList list = new ArrayList{"First","Second","Third"};
IEnumerable<string> strings = list.Cast<string>();
foreach (var item in strings)
{
System.Console.WriteLine(item);
}
list = new ArrayList{1,"not an int",2,3};
IEnumerable<int> ints = list.OfType<int>();
foreach (var VARIABLE in ints)
{
System.Console.WriteLine(VARIABLE);
}
}

listing11.6 Using an explicitly typed range variable to automatically call Cast

1
2
3
4
5
6
7
8
9
10
11
12
public void List116()
{
ArrayList list = new ArrayList{"First","Second","Third"};
var strings = from string entry in list
select entry.Substring(0, 3);
foreach (var s in strings)
{
System.Console.WriteLine(s);
}
}
//translation more interesting
list.Cast<string>().Select(entry=>entry.Substring(0,3));

what is important

  • LINQ 以数据序列为基础,在任何可能的地方都是流处理
  • 创建一个查询并不会立即执行它: 大部分操作都会延迟执行
  • C#3 的查询表达式包括一个把表达式转换成普通C#代码的预处理阶段,接着使用类型推断、重载、Lambda表达式等这些常规规则来恰当地对转换后的代码进行编译
  • 在查询表达式中声明的变量的作用:他们仅仅是范围变量,通过他们你可以在查询表达式内部一致地引用数据

11.3 Filtering and ordering a sequence

listing 11.7 Query expression usig mutiple where clauses

1
2
3
4
5
6
7
8
9
10
11
12
13
User tim = SampleData.Users.TesterTim;
var query = from defect in SampleData.AllDefects
where defect.Status != Status.Closed
where defect.AssignedTo == tim
select defect.Summary;
foreach (var su in query)
{
System.Console.WriteLine(su);
}
translated in
SampleData.AllDefects.Where(defect => defect.Status != Status.Closed)
.Where(defect => defect.AssignedTo == tim)
.Select(defect => defect.Summary);

listing 11.8 Sorting by the severity of a defect, from high to low priority

1
2
3
4
5
6
7
8
9
10
11
12
13
public void List118()
{
User tim = SampleData.Users.TesterTim;
var query = from defect in SampleData.AllDefects
where defect.Status != Status.Closed
where defect.AssignedTo == tim
orderby defect.Severity descending
select defect;
foreach (var de in query)
{
System.Console.WriteLine("{0} : {1} ",de.Severity,de.Summary);
}
}

listing 11.9 Ordering by severity and then last modified time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void List119()
{
User tim = SampleData.Users.TesterTim;
var query = from defect in SampleData.AllDefects
where defect.Status != Status.Closed
where defect.AssignedTo == tim
orderby defect.Severity descending, defect.LastModified
select defect;
foreach (var defect in query)
{
System.Console.WriteLine("{0} : {1} ({2:d})",defect.Severity,defect.Summary,defect.LastModified);
}
}
translation
SampleData.AllDefects.Where(defect => defect.Status != Status.Closed)
.Where(defect => defect.AssignedTo == tim)
.OrderByDescending(defect => defect.Severity)
.ThenBy(defect => defect.LastModified);

11.4 Let clauses and transparent identifiers

listing 11.10 Sorting by the lengths of user names without a let clause

1
2
3
4
5
6
7
var query = from user in SampleData.AllUsers
orderby user.Name.Length
select user.Name;
foreach (var name in query)
{
System.Console.WriteLine("{0}: {1}",name.Length,name);
}

listing 11.11 use let

1
2
3
4
5
6
7
8
9
10
11
12
13
var query = from user in SampleData.AllUsers
let length = user.Name.Length
orderby length
select new {Name = user.Name, Length = length};
foreach (var entry in query)
{
System.Console.WriteLine("{0} : {1}",entry.Length,entry.Name);
}
//translation
SampleData.AllUsers
.Select(user => new {user, length = user.Name.Length})
.OrderBy(z => z.length)
.Select(z => new {Name = z.user.Name, Length = z.length});

11.5 Joins

listing 11-12

1
2
3
4
5
6
7
8
var query = from defect in SampleData.AllDefects
join subscription in SampleData.AllSubscriptions
on defect.Project equals subscription.Project
select new {defect.Summary, subscription.EmailAddress};
foreach (var entry in query)
{
System.Console.WriteLine("{0} : {1} ",entry.EmailAddress,entry.Summary);
}

listing 11.13 Joining defects and subscriptions with a group join

1
2


listing 11.15

1
2
3
4
5
6
7
8
9
10
public void List1115()
{
var query = from user in SampleData.AllUsers
from project in SampleData.AllProjects
select new {User = user, Project = project};
foreach (var pair in query)
{
System.Console.WriteLine("{0} / {1} ",pair.User.Name,pair.Project.Name);
}
}

listing 11.16

1
2
3
4
5
6
7
8
9
10
11
12
13
public void List1116()
{
var query = from left in Enumerable.Range(1, 5)
from right in Enumerable.Range(11, left)
select new {Left = left, Right = right};
foreach (var pair in query)
{
System.Console.WriteLine("Left={0}; Right ={1}",pair.Left,pair.Right);
}
}
//
SampleData.AllDefects.Where(defect => defect.AssignedTo != null)
.GroupBy(defect => defect.AssignedTo);

11.6 Groupings and continuations

listing 11.17 Grouping defects by assignee - trivial projection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void List1117()
{
var query = from defect in SampleData.AllDefects
where defect.AssignedTo != null
group defect by defect.AssignedTo;
foreach (var entry in query)
{
System.Console.WriteLine(entry.Key.Name);
foreach (var defect in entry)
{
System.Console.WriteLine("({0}) {1}",defect.Severity,defect.Summary);
}

System.Console.WriteLine();
}
}
//

listing 11.18 Grouping defects by assignee – projection retains just the summary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void List1118()
{
var query = from defect in SampleData.AllDefects
where defect.AssignedTo != null
group defect.Summary by defect.AssignedTo;
foreach (var entry in query)
{
System.Console.WriteLine(entry.Key.Name);
foreach (var defect in entry)
{
System.Console.WriteLine("({0}) ",defect);
}

System.Console.WriteLine();
}
}
//
SampleData.AllDefects.Where(defect => defect.AssignedTo != null)
.GroupBy(defect => defect.AssignedTo, defect => defect.Summary);

11.7 Choosing between query expressions and dot notation

listing 11.19 Continuing a grouping with another projection

1
2
3
4
5
6
7
8
9
10
11
12
public void List1119()
{
var query = from defect in SampleData.AllDefects
where defect.AssignedTo != null
group defect by defect.AssignedTo
into grouped
select new {Assignee = grouped.Key, Count = grouped.Count()};
foreach (var entry in query)
{
System.Console.WriteLine("{0}: {1}",entry.Assignee.Name,entry.Count);
}
}

listing 11.20 Query expression continuations from group and select\

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void List1120()
{
var query = from defect in SampleData.AllDefects
where defect.AssignedTo != null
group defect by defect.AssignedTo
into grouped
select new {Assignee = grouped.Key, Count = grouped.Count()};
foreach (var entry in query)
{
System.Console.WriteLine("{0} : {1}",entry.Assignee.Name,entry.Count);
}

SampleData.AllDefects.Where(defect => defect.AssignedTo != null)
.GroupBy(defect => defect.AssignedTo)
.Select(grouped => new {Assignee = grouped.Key, Count = grouped.Count()})
.OrderByDescending(result => result.Count);
}

11.8 Summary

在合适的时候使用点标记

C# 4:Playing nicely with others

13 minor changes to simplify code

  • Optional parameters
  • Named arguments
  • Streamlining ref parameters in COM
  • Embedding COM Primary Interop Assemblies
  • Calling named indexers declared in COM
  • Generic variance for inter faces and delegates
  • Changes in locking and field-like events

13.1 Optional parameters and named arguments

listing 13.1 Declaring and calling a method with optional parameters

1
2
3
4
public void List1301(int x,int y =20 ,int z =30)
{
System.Console.WriteLine("x={0},y={1},z={2}",x,y,z);
}

listing 13.2 Using null default values to handle nonconstant situations

1
2
3
4
5
6
7
8
9
10
11
12
13
public void List1302(string filename, string message, Encoding encoding = null, DateTime? timestamp = null)
{
Encoding realEncoding = encoding ?? Encoding.UTF8;
DateTime realTimestamp = timestamp ?? DateTime.Now;
using (TextWriter writer = new StreamWriter(filename, true, realEncoding))
{
writer.WriteLine("{0:s} : {1}",realTimestamp,message);
}
}
...
new CinD().List1302("utf8.txt","First message");
new CinD().List1302("ascii.txt","ASCII",Encoding.ASCII);
new CinD().List1302("utf8.txt","message in the future",null,new DateTime(2030,1,1));

listing 13.3 Simple examples of using named arguments

1
2
3
4
5
6
7
8
9
10
11
12
13
static void Dump(int x, int y, int z)
{
System.Console.WriteLine("x={0} y={1} z={2} ",x,y,z);
}
...
static void Main(string[] args)
{
Dump(1,2,3);
Dump(x:1,y:2,z:3);
Dump(z:3,y:2,x:1);
Dump(1,y:2,z:3);
Dump(1,z:3,y:2);
}

listing 13.4 Logging argument evaluation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static void Main(string[] args)
{
Dump(x:Log(1),y:Log(2),z:Log(3));
Dump(z:Log(3),x:Log(1),y:Log(2));
}

static int Log(int value)
{
System.Console.WriteLine("Log: {0}",value);
return value;
}
static void Dump(int x, int y, int z)
{
System.Console.WriteLine("x={0} y={1} z={2} ",x,y,z);
}
//re
log 1 2 3
log 3 1 2

listing 13.5 Abusing argument evaluation order

1
2
3
4
int i = 0;
Dump(x:++i,y:++i,z:++i);
i = 0;
Dump(x:++i,y:++i,z:++i);

listing 13.6 Combining named arguments and optional parameters

1
2
3
4
5
6
7
8
9
10
11
static void AppendTimestamp(string filename,string message,Encoding encoding = null, DateTime? timestamp = null)
{
Encoding realEncoding = encoding ?? Encoding.UTF8;
DateTime realTimestamp = timestamp ?? DateTime.Now;
using (TextWriter writer = new StreamWriter(filename, true, realEncoding))
{
writer.WriteLine("{0:s} : {1}",realTimestamp,message);
}
}
///
AppendTimestamp("utf8.txt","Message in the future",timestamp: new DateTime(2030,1,1));

listing 13.7

1
2


13.2 Improvements for COM interoperablity

listing 13.8 Creating and saving a documents in C# 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object missing = Type.Missing;
Application app = new Application {Visible = true};
app.Documents.Add(ref missing, ref missing, ref missing, ref missing);
Document doc = app.ActiveDocument;
Paragraph para = doc.Paragraphs.Add(ref missing);
para.Range.Text = "Thank goodness for C# 4";

object filename = "demo.doc";
object format = WdSaveFormat.wdFormatDocument97;
doc.SaveAs(ref filename,ref format,ref missing,ref missing,ref missing,
ref missing,ref missing,ref missing,
ref missing,ref missing,ref missing,
ref missing,ref missing,ref missing,
ref missing,ref missing);
doc.Close(ref missing,ref missing,ref missing);
app.Application.Quit(ref missing,ref missing,ref missing);

listing 13.9 Automating Word using normal C# 4 features

1
2
3
4
5
6
7
8
9
10
Application app = new Application {Visible = true};
app.Documents.Add();
Document doc = app.ActiveDocument;
Paragraph para = doc.Paragraphs.Add();
para.Range.Text = "Thank goodness for C# 44444444444444444444";
object filename = "demo1.doc";
object format = WdSaveFormat.wdFormatDocument97;
doc.SaveAs(FileName:ref filename,FileFormat:ref format);
doc.Close();
app.Application.Quit();

listing 13.10 Passing arguments by value in COM methods

1
2
3
4
5
6
7
8
Application app = new Application {Visible = true};
app.Documents.Add();
Document doc = app.ActiveDocument;
Paragraph para = doc.Paragraphs.Add();
para.Range.Text = "Thank goodness for C# 4";
doc.SaveAs(FileName:"text.doc",FileFormat:WdSaveFormat.wdFormatDocument97);
doc.Close();
app.Application.Quit();

listing 13.11 Error– Using the SynonymInfo indexer to count word meanings

1
2
3
4
5
6
7
8
9
10
11
static void List1311(SynonymInfo info)
{
Console.WriteLine("{0} has {1} meanings ",info.Word,info.MeaningCount);
}
...
Application app = new Application {Visible = false};
object missing = Type.Missing;
List1311(app.get_SynonymInfo("painful",ref missing));
List1311(app.SynonymInfo["nice",WdLanguageID.wdEnglishUS]);
List1311(app.SynonymInfo[Word:"features"]);
app.Application.Quit();

13.3 Generic variance for interfaces and delegates

协变性和逆变性

1 协变

1
2
3
4
interface IFactory<T>
{
T CreateInstance();
}

如在现实世界中, 你可以把比萨工厂视为食品工厂

2 逆变

1
2
3
4
interface IPrettyPrinter<T>
{
Void Print(T document);
}

如果我们实现了IPrettyPrinter,就可以当做IPrettyPrinter来使用

3 不变

1
2
3
4
5
interface IStorage<T>
{
byte[] Serialize(T value);
T Deserialize(byte[] data);
}

listing 13.12 Using variance to build a list of general shapes from specific lists

1
2
3
4
List<IShape> shapesByAdding = new List<IShape>();
shapesByAdding.AddRange(circles);
shapesByAdding.AddRange(squares);
List<IShape> shapesByConcat = circles.Concat<IShape>(squares).ToList();

listing 13.13 Sorting circles using a general-purpose comparer and contravariance

1
2
3
4
5
6
7
8
9
10
class AreaComparer: IComparer<IShape>
{
public intCompare(IShape x,IShape y)
{
return x.Area.CompareTo(y.Area);
}
}
...
IComparer<IShape> areaComparer = new AreaComparer();
circles.Sort(areaComparer);

Sort的参数应该为IComparer类型,但是逆变性会进行隐式转换。就这么简单

listing 13.14 Using variance with simple Func and Action delegates

1
2
3
4
5
6
7
Func<Square> squareFactory = ()=> new Square(new Point(5,5),10);
Func<IShape> shapeFactory = squareFactory;
Action<IShape> shapePrinter = shape => Console.WriteLine(shape.Area);
Action<Square> squarePrinter = shapePrinter;

squarePrinter(squareFactory());
shapePrinter(shapeFactory());

13.4 Teeny tiny changes to locking and field-like events

listing 13.15 Demonstrating covariance and contravariance with a single type

Converter<TInput,TOutput> 同时使用协变性 和逆变性

1
2
3
4
5
Converter<object, string> converter = x => x.ToString();
Converter<string, string> contravariance = converter;

Converter<object, object> convariance = converter;
Converter<string, object> both = converter;
1
2
3
4
delegate Func<T> FuncFunc<out T>();
delegate void ActionAction<out T>(Action<T> action);
delegate void ActionFunc<int T> (Func<T> function);
delegate Action<T> FuncAction<int T>();

13.5 Summary