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 | using System.Collections; |
1.1.2 Strongly typed collections in C#2
The most important change in C# 2: generics
1 | // -------------------------------------------------------------------------------------------------------------------- |
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 | namespace C01 |
1.1.4 Name arguments in C# 4
This gives you the clarity of C# 3 initializers without the mutability.
1 | using System.Collections.Generic; |
Here is the summarizes.
1.2 Sorting and filtering
1.2.1 Sorting products by name
if you use this
1 | using System; |
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 | [Description("Listing 1.05")] |
The following listing shows how to do precisely this,telling the Sort method how to compare two products using a delegate.
1 | class ArrayListSort |
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 | List<Product> products = Product.GetSampleProducts(); |
You’re able to call the OrderBy method due to the presence of an extension method.
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 | ArrayList products= Product.GetSampleProducts(); |
The following listing demonstrates how C#2 lets you flatten things out a bit.
Listing 1.11 Separating testing from printing (C# 2)
1 | List<Product> products = Product.GetSampleProducts(); |
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 | List<Product> products = Product.GetSampleProducts(); |
Listing 1.13 Testing with a lambda expression(C# 3)
1 | List<Product> products = Product.GetSampleProducts(); |
1.3.1 Representing an unknown price
.NET 2.0 makes matters a lot simpler by introducing the Nullable
1 | decimal?price; |
Listing 1.14 Displaying products with an unknown price(C# 3)
1 | List<Product> products = Product.GetSampleProducts(); |
the C# 2 code
1 | List<Product> products = Product.GetSampleProducts(); |
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 | public Product(string name,decimal? price=null) |
1.4 Introducing LINQ
1.4.1 Query expressions and in-process queries
Listing 1.15 First steps with query expressions:filtering acollection
1 | List<Product> products = Product.GetSampleProducts(); |
Listing 1.16
1 | List<Product> products = Product.GetSampleProducts(); |
Listing 1.19
1 | using Microsoft.Office.Interop.Excel; |
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 | StringProcessor proc1,proc2; |
调用委托实例
void Invoke(string input)
1 | using System;; |
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 | object o ="hello"; |
可以说o的静态类型为System.Object.
动态类型的实质是变量中含有值,但是那些值并不限于特定的类型,所以编译器不能执行相同形式的检查。
对一个动态语言 可以
1 | o="hello"; |
EXPLICIT TYPING VERSUS IMPLICIT TYPING
1 | var s="hello" |
这在C#1中是不行的
TYPE-SAFE VERSUS TYPE-UNSAFE
C 例子
1 |
|
2.2.2 When is C# 1’s type ststem not rich enough?
?:skier:
- 集合,强和弱
- 缺乏协变的返回类型
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 | int i=5; |
第二句装箱 第三局拆箱
装拆都不会改变原始值
将值作为接口表达式时也会装箱
IComparable x=5;
2.3.5 值类型和引用类型小结
- 对于引用类型的表达式,它的值是一个引用,而非对象
- 引用就像URL-是允许你访问真实信息的一小片数据
- 对于值类型的表达式,他的值就是实际的数据
- 有时,值类型比引用类型更有效,有时恰好相反
- 引用类型的对象总是在堆上,值类型的值既可能在堆上 也可能在栈上
- 引用类型作为方法参数使用时,参数默认是以值传递 方式来传递的–但值本身是一个引用
- 值类型的值会在需要引用类型的行为时被装箱;拆箱是相反的过程。
2.4 C#1之外:构建于坚实基础之上的新特性
2.4.1 与委托有关的特性
1 | static void HandleDemoEvent(object sender,EventArgs e) |
Lambda表达式
1 | Func<int ,int,string > func =(x,y)=>(x*y).ToString(); |
###2.4.2 Features related to the type system
匿名类型和隐式类型
1 | var jon = new {Name ="Jon",Age = 31}; |
C#4中的动态类型
1 | dynamic o ="hello"; |
2.4.2 Features related to value types
- 泛型
- 可空类型
1 | int? x= null; |
###2.5 Summary
3 Parameterized typing with generics
3.1 why generics are necessary
1 | static Dictionary<string,int> CountWords(string text) |
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 | static double TakeSquareRoot(int x) |
Listing 3.3
1 | static List<T> MakeList<T> (T first,T second) |
###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 | public T CreateInstance<T>() where T: new() |
Valid : CreateInstance<int>()
CreateInstance<object>
CONVERSION TYPE CONSTRAINTS
1 | class Sample<T> where T: Stream, |
可以指定多个接口,但是只能指定一个类.
COMBINING CONSTRAINTS
Valid:
1 | class Sample<T> where T:class,IDisposable,new() |
Invalid:
1 | class Sample<T> where T:class,struct |
3.3.2 Type inference for type arguments of generic methods
1 | static List<T> MakeList<T>(T first,T second) |
3.3.3 Implementing generics
DEFAULT VALUE EXPRESSIONS
listing 3.4 Comparing a given value to the default in a generic way
1 | static int CompareToDefault<T> (T value) where T:IComparable<T> |
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 | static bool AreReferencesEqual<T>(T first ,T second) |
FULL COMPARISON EXAMPLE:REPRESENTING A PAIR OF VALUES
Listing 3.6 Generic class representing a pair of values
1 | using System; |
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 | class TypeWithField<T> |
Listing 3.9 Static constructors with nested generic types
1 | public class Outer<T> |
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 | class CountingEnumerable:IEnumerable<int> |
3.4.4 Reflection and generics
USING TYPEOF WITH GENERIC TYPES
listing 3.11
1 | static void DemonstrateTypeof<X>() |
listing 3.12
1 | string listTypeName="System.Collections.Generic.List`1"; |
System.Type的属性和方法
有两个方法是最重要的GetGenericTypeDefinition和MakeGenericType(作用于泛型类型定义,返回一个已构造类型)
- 反射泛型方法
通过反射来调用泛型方法
Listing 3.13
1 | public static void PrintTypeParameter<T>() |
3.5 Limitations of generics in C# and other languages
3.5.1 Lack of generic variance
泛型是不变体
- 协变性在什么时候有用
- 没理解
####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 | static void Display(Nullable<int> 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 | int? nullable = 5; |
强调一波 代码最终是要给人读的 所以可读性和规范化很重要
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 | public TimeSpan get_Age() |
4.3.2 Assigning and comparing with null
4.3.3 Nullable conversions and operators
可空类型也是可以转换的
1 | int i=5; |
这玩意会发出警告,因为两者都存在一个到int?的隐式转换
4.3.4 Nullable logic
4.3.5 Using the as operator with nullable types
1 | static void PrintValueAsInt32(object o) |
然而.net 4.5 以前is 和强制转换比 as快20倍
4.3.6 The null coalescing operator
空合并操作符
这个二元操作符在对first??second求值时:
- 对first进行求值
- 如果结果非空,则该结果就是整个表达式的结果
- 否则求second的值,其结果作为整个表达式的结果
之前的死亡日期属性
1 | DateTime lastAlive = death??DateTime.Now |
涉及空值的时候就可以这么写
1 | Address contact = user.COntractAddress; |
4.4 Novel uses of nullable types
4.4.1 Tyeing an operation without using output parameter
1 | static int? TryParse(string text) |
后面的方法更为条理清晰
####4.4.2 Painless comparisons with the null coalescing operator
1 | public int Compare(Product first, Product second) |
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 | static void LogPlainEvent(object sender, EventArgs e) |
5.2 Method group conversions
5.3 Covariance and contravariance
list5.2
1 | ... |
5.3.1 Contravariance for delegate parameters
5.3.2 Covariance of delegate return types
1 | delegate Stream StreamFactory(); |
5.3.3 A small risk of incompatibility
1 | delegate void SampleDelegate(string x); |
5.4 Inline delegate actions with anonymous methods
5.4.1 Starting simply : acting on a parameter
list5.5
1 | Action<string> printReverse = delegate(string text) |
1 | //三级跳 |
5.4.2 Returing values from anonymous methods
list 5.7
1 | Predicate<int> isEven = delegate(int x) { return x % 2 == 0; }; |
新的语法和我们所期望的几乎完全相符__不需要在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 | SortAndShowFiles( |
5.4.3 Ignoring delegate parameters
list 5.6 代码精简
1 | List<int> x = new List<int>(); |
list 5.9 Subcribing to events with anonymous methods that ignore parameters
1 | Button button = new Button(); |
5.5 Capturing variables in anonymous methods
list 5.10 Examples of variable kinds with respect to anonymous methods
1 | static void EnclosingMethod() |
5.5.1 Defining closures and different types of variables
5.5.2 Examining the behavior of captured variables
1 | string captured = "Before x is captured"; |
1 | List<Person> FindAllYoungerThan(List<Person> people,int 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 | static void Main(string[] args) |
A captured varibale lives for at least as long as any delegate instance referring to it.
我们一般会人为counter在栈上,实际上在堆上?
####5.5.5 Local vairable instantiations
比较这两段代码
1 | int single; |
后者被创建了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 | class Program |
6.2 C#2 Simple iterators with yield statements
listing 6.4 Iterating through the sample collection with c#2 and yield return
1 | public IEnumerator GetEnumerator() |
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 | private static readonly string Padding = new string(' ', 30); |
- 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 | static IEnumerable<int> CountWithTimeLimit(DateTime limit) |
listing6.7 Demonstration of yield break working with try/finally
1 |
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 | static IEnumerable<string> ReadLines(string filename) |
6.3.3 Filtering items lazily using an iterator block and a predicate
1 | public static IEnumerable<T> Where<T>(IEnumerable<T> source, Predicate<T> predicate) |
我们将实现分为两部分: 参数验证和真正的过滤逻辑.假设将所有内容放在同一个方法里,那么调用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 | partial class PartialMethodDemo |
listing 7.3 sealed class NonStarticStringHelper
1 | public sealed class NonStaticsStringHelper // seals class to prevent derivation |
listing 7. 4 The same utility class as in listing 7.3 but converted into a c#2 static class
1 | public static class StringHelper |
7.3 Separate getter/setter property access
listing
1 | private string name; |
7.4 Namespace class
在C#2中,有三种别名种类: C#1 的命名空间别名/ 全局命名空间别名和外部别名.
listing 7.5 Using aliases to distinguish between different button types
1 | using WinForms = System.Windows.Forms; |
listing 7.6 use : : to tell the compiler to use aliases
1 | using WinForms = System.Windows.Forms; |
7.4.2 The global namespace alias
listing 7.7 Use of the global namespace alias to specify the desired type exactly
1 | class Configuration { } |
7.4.3 Extern aliases
Mark!
listing 7.8 Working with different types of the same type in different assemblies
1 | extern alias FirstAlias; |
7.5 Pragma directives
7.5.1 Warning pragmas
有时候你需要忽略一个警告
listing 7.9 Class containing an unused field
1 | public class FieldUsedOnlyByReflection |
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 | namespace Fixedsizebuffers |
7.7 Exposing internal members to selected assemblies
Listing 7.11 Demonstration of fixed-size bufer to obtain console color information
1 | //Compiled to Source.dll |
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 | public class Person |
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 | public class Person |
8.3.2 Setting simple properties
1 | Person tom = new Person("Tom") |
8.3.3 Setting properties on embedded objects
8.3.4 Collection initializers
1 | var names = new List<string> { "Holly", "Jon", "Tom", "Robin", "William" }; |
listing 8.3 Building up a rich object using object and collection initializers
1 | Person tom = new Person |
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 | var tom = new {Name ="Tom",Age=9}; |
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 | var family = new [] |
8.5.2 members of anonymous types
8.5.3 Projection initializers
1 | List<Person> family = new List<Person> |
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 | Func<string,int> returnLength; |
list 9.2 A long-winded first lambda expression,similar to an anonymous method
1 | Func<string,int> returnLength; |
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 | class Film |
9.2.2 Logging in an event handler
Listing9.5 Logging events using lambda expressions
1 | static void Log(string title, object sender, EventArgs e) |
9.3 Expression trees
9.3.1 Building expressiono trees programmatically
listing 9.6 A simple expression tree ,adding 2 and 3;
1 | Expression firstArg = Expression.Constant(2); |
9.3.2 Compiling expression trees into delegates
listing 9.7 Compiling and executing an expression tree
1 | Expression firstArg = Expression.Constant(2); |
9.3.3 COnverting C# lambda expressions to expression trees
listing 9.8 Compliing and executing an expression tree
1 | Expression<Func<int>> return5 = () => 5; |
listing 9.9 Demonstration of a more complicated expression tree
1 | Expression<Func<string, string, bool>> expression = (x, y) => x.StartsWith(y); |
listing 9.10 Building a method call expression tree in code
1 | MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) }); |
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 | static void PrintConvertedValue<TInput,TOutput> |
9.4.2 Inferred return types of anonymous functions
listing 9.12 Attempting to infer the return type of an anonymous method
1 | delegate T MyFunc<T>(); |
listing 9.13 Code returning an integer or an object depending on the time of day
1 | delegate T MyFunc<T>(); |
9.4.3 Two-phase type inference
listing 9.14 Flexible type inference combining information from multiple arguments
1 | static void PrintType<T>(T first, T second) |
listing 9.15 Multistage type inferenct
1 | static void ConvertTwice<TInput, TMiddle, TOutput>( |
listing 9.16 Sample of overloading choice influenced by delgate return type
1 | static void Execute(Func<int> action) |
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 | public class StreamUtil |
listing 10.2 Using StreamUtil to copy a web response stream to a file
1 | static void Main(string[] args) |
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 | public class StreamUtil |
10.2.2 Calling extensio methods
listing 10.4 Copying a stream using an extension method
1 | static void Method2() |
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 | public static class NullUtil |
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 | ... |
listing 10.7 Reversing a collection with the Reverse method
1 | ... |
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 | var collection = Enumerable.Range(0, 10).Where(x => x % 2 != 0).Reverse(); |
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 | var collection = Enumerable.Range(0, 10) |
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 | var collection = Enumerable.Range(-5, 11) |
10.3.6 Business examples involving chaining
1 | company.Departments |
1 | bugs.GroupBy(bug => bug.AssignedTo) |
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 | var query = from user in SampleData.AllUsers |
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 | class TranslationExample{ |
listing 11.4 Query selecting just the names of the users
1 | IEnumerable<string> query = from user in SampleData.AllUsers select user.Name; |
11.2 Simple beginnings : selecting elements
listing11.5 using Cast and OfType to work with weakly typed collections
1 | public void List115() |
listing11.6 Using an explicitly typed range variable to automatically call Cast
1 | public void List116() |
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 | User tim = SampleData.Users.TesterTim; |
listing 11.8 Sorting by the severity of a defect, from high to low priority
1 | public void List118() |
listing 11.9 Ordering by severity and then last modified time
1 | public void List119() |
11.4 Let clauses and transparent identifiers
listing 11.10 Sorting by the lengths of user names without a let clause
1 | var query = from user in SampleData.AllUsers |
listing 11.11 use let
1 | var query = from user in SampleData.AllUsers |
11.5 Joins
listing 11-12
1 | var query = from defect in SampleData.AllDefects |
listing 11.13 Joining defects and subscriptions with a group join
1 |
listing 11.15
1 | public void List1115() |
listing 11.16
1 | public void List1116() |
11.6 Groupings and continuations
listing 11.17 Grouping defects by assignee - trivial projection
1 | public void List1117() |
listing 11.18 Grouping defects by assignee – projection retains just the summary
1 | public void List1118() |
11.7 Choosing between query expressions and dot notation
listing 11.19 Continuing a grouping with another projection
1 | public void List1119() |
listing 11.20 Query expression continuations from group and select\
1 | public void List1120() |
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 | public void List1301(int x,int y =20 ,int z =30) |
listing 13.2 Using null default values to handle nonconstant situations
1 | public void List1302(string filename, string message, Encoding encoding = null, DateTime? timestamp = null) |
listing 13.3 Simple examples of using named arguments
1 | static void Dump(int x, int y, int z) |
listing 13.4 Logging argument evaluation
1 | static void Main(string[] args) |
listing 13.5 Abusing argument evaluation order
1 | int i = 0; |
listing 13.6 Combining named arguments and optional parameters
1 | static void AppendTimestamp(string filename,string message,Encoding encoding = null, DateTime? timestamp = null) |
listing 13.7
1 |
13.2 Improvements for COM interoperablity
listing 13.8 Creating and saving a documents in C# 3
1 | object missing = Type.Missing; |
listing 13.9 Automating Word using normal C# 4 features
1 | Application app = new Application {Visible = true}; |
listing 13.10 Passing arguments by value in COM methods
1 | Application app = new Application {Visible = true}; |
listing 13.11 Error– Using the SynonymInfo indexer to count word meanings
1 | static void List1311(SynonymInfo info) |
13.3 Generic variance for interfaces and delegates
协变性和逆变性
1 协变
1 | interface IFactory<T> |
如在现实世界中, 你可以把比萨工厂视为食品工厂
2 逆变
1 | interface IPrettyPrinter<T> |
如果我们实现了IPrettyPrinter
3 不变
1 | interface IStorage<T> |
listing 13.12 Using variance to build a list of general shapes from specific lists
1 | List<IShape> shapesByAdding = new List<IShape>(); |
listing 13.13 Sorting circles using a general-purpose comparer and contravariance
1 | class AreaComparer: IComparer<IShape> |
Sort的参数应该为IComparer
listing 13.14 Using variance with simple Func
and Action delegates
1 | Func<Square> squareFactory = ()=> new Square(new Point(5,5),10); |
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 | Converter<object, string> converter = x => x.ToString(); |
1 | delegate Func<T> FuncFunc<out T>(); |