2011年7月15日 星期五

The C# Programming Language (3rd Edition)

C#程序設計語言(原書第3版)/開發人員專業技術叢書


Bear在這裡買的
amazon的參考
-----------------------------------------------------------
作者:(美)海傑爾斯伯格|譯者:顧雁宏//徐旭銘
出版社:機械工業
ISBN:9787111282617
出版日期:2010/01/01
裝幀:
頁數:548
人民幣:RMB 79 元
-----------------------------------------------------------



P.2(原文P.)
BRAD ABRAMS
請注意:有趣的是這裡的Console.WriteLine()其實是Console.Out.WriteLine()的簡寫。Console.Out是一個屬性,它返回了一個設計用來專門向控制台輸出的System.IO.TextWriter基類的實現。

It is interesting to note that Console.WriteLine() is simply a shortcut for Console.Out.WriteLine.Console.Out is a property that returns an implementation of the System.IO.TextWriter base class designed to output to the console. The previous example could be written equally correctly as follows:

using System;
class Hello
{
static void Main() {
Console.Out.WriteLine("Hello, World");
}
}

P.6(原文P.)
C#支持任意類型的一維和多維數組。和之前那些類型不同,數組類型不需要在使用前聲明,它們可以通過在型名稱後面加上方括號而構造出來。例如,int[]是一個一維的int數值,int[,]則是二維的int數組,而int[][]是一個嵌套了一維數組的數組。

C# supports single- and multi-dimensional arrays of any type. Unlike the types listed previously, array types do not have to be declared before they can be used. Instead, array types are constructed by following a type name with square brackets. For example, int[] is a single-dimensional array of int, int[,] is a two-dimensional array of int, and int[][] is a single-dimensional array of single-dimensional arrays of int.

P.7(原文P.)
當值類型的值被轉換成object實例(也叫箱子)被分配來保存這個值,然後將這個值複制到箱子裡去。反過來,當一個對象引用被轉換成值類型時,首先要檢查引用對象是不是正確值類型的箱子,如果是的話,箱子裡的值就會被複制出來。

When a value of a value type is converted to type object, an object instance, also called a "box," is allocated to hold the value, and the value is copied into that box. Conversely, when an object reference is cast to a value type, a check is made that the referenced object is a box of the correct value type; if the check succeeds, the value in the box is copied out.

P.10(原文P.)
switch語句 原文就有錯誤了,多了一個}。

P.14(原文P.)
KRZYSZTOF CWALINA
注意C#的public關鍵字和C++裡的public是有區別的!在C++裡,它意味著"在我的編譯單元內部"。而在C#裡,它的意思和C++裡的extern一樣(即,任何人都可以調用它)。它個區別是非常大的!

People need to be careful with the public keyword. public in C# is not equivalent to public in C++! In C++, it means "internal to my compilation unit." In C#, it means what extern meant in C++ (i.e., everybody can call it). This is a huge difference!

P.14(原文P.)
ERIC LIPPERT
protected internal已經證實是一個爭議很大而且有點失敗的選擇。很多人都沒有正確使用這個特性,他們以為protected internal的意思是"只有在這個程序裡繼承的子類才能訪問"。就是說,他們以為是protected 和 internal,而實際上它的意思應該是protected 或者 internal。

protected internal has proven to be a controversial and somewhat unfortunate choice. Many people using this feature incorrectly believe that protected internal means "access is limited to classes derived from this class within this program." That is, they believe it means protected and internal, when, in fact, it means protected or internal.

Were a hypothetical future version of the C# language to provide a syntax for "protected and internal," the question would then be which combination of keywords would have that meaning. I am holding out for either "proternal" or "intected," but I suspect I will have to live with disappointment.

P.15(原文P.)
中間有錯字 Point#D 應該是 Point3D 才對

P.16-17(原文P.)
ERIC LIPPERT
在泛型類型的情況下,每個構造類型都有自已的靜態字段。這就是說,如果有一個類

Static fields are per constructed type for a generic type. That is, if you have a

class Stack { public readonly static Stack empty = whatever; ... }

那麼Stack.empty和Stack.empty是不同的。

then Stack.empty is a different field than Stack.empty.

public class Color
{
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);

private byte r, g, b;

public Color(byte r, byte g, byte b) {
this.r = r;
this.g = g;
this.b = b;
}
}

Bear:可以參考一下這個例子中的技巧,在static 屬性中利用new 自已的實體方式並回傳出去,以達到靜態呼叫實體這個技巧。

BRAD ABRAMS
readonly保護的是字段的位置(而非那個位置上的值)不會在構造函數之外被改變。

readonly protects the location of the field from being changed outside of the type's constructor but not the value at that location. For example, consider the following type:

public class Names
{
public static readonly StringBuilder FirstBorn = new
StringBuilder("Joe");
public static readonly StringBuilder SecondBorn = new
StringBuilder("Sue");
}

在構造函數之外,直接修改FirstBorn實例都會導致編譯錯誤:

Outside of the constructor, directly changing the FirstBorn instance results in a compiler error:

Names.FirstBorn = new StringBuilder("Biff"); // Compile error

但是修改StringBuilder實例卻完全沒有問題:

However, I am able to accomplish exactly the same results by modifying the StringBuilder instance:

Names.FirstBorn.Remove(0,6).Append("Biff");
Console.WriteLine(Names.FirstBorn); // Outputs "Biff"

基於這個原因,我們強烈建議只讀字段的使用應該被限制在不可變的類型上。不可變類型沒有公開暴露任何setter方法來讓你改變它的值。

It is for this reason that we strongly recommend that read-only fields be limited to immutable types. Immutable types do not have any publicly exposed setters, such as int, double, or String.

P.17(原文P.)
ERIC LIPPERT
不幸的是,對於泛型類型,構造類型有可能會生成兩個簽名完全一樣的方法。例如

An unfortunate consequence of generic types is that a constructed type may potentially have two methods with identical signatures. For example,

class C { void M(T t){} void M(int t){} ... }

這個定義本身是完全合法的,但是C有兩個簽名一模一樣的方法M。

is perfectly legal, but C has two methods M with identical signatures. As we'll see later on, this leads to some interesting scenarios involving overload resolution and explicit interface implementations.

P.18(原文P.)
ERIC LIPPERT
引用參數則是一種稍有不同的"傳遞作用"。在這裡,引用指向的是變量本身,而不是某個對象實例。如果變量包含的碰巧是一個值類型的話(比如上面的例子),那也是完全合法的。這個值並不是按引用傳遞的,按引用傳遞的是持有它的那個變量。

Reference parameters are a slightly different form of "passing by reference." In this case, the reference is to the variable itself, not to some object instance. If that variable happens to contain a value type (as shown in the previous example), that's perfectly legal. The value is not being passed by reference, but rather the variable that holds it is.

只有方法的最後一個參數才可以是參數數組,並且參數數組的類型必須是一維的數組類型。

A parameter array permits a variable number of arguments to be passed to a method. It is declared with the params modifier. Only the last parameter of a method can be a parameter array, and the type of a parameter array must be a single-dimensional array type. The Write and WriteLine methods of the System.Console class are good examples of parameter array usage. They are declared as follows:

P.20(原文P.)
The following Entity class has both static and instance members.

class Entity
{
static int nextSerialNo;

int serialNo;

public Entity() {
serialNo = nextSerialNo++;
}

public int GetSerialNo() {
return serialNo;
}

public static int GetNextSerialNo() {
return nextSerialNo;
}

public static void SetNextSerialNo(int value) {
nextSerialNo = value;
}
}

Each Entity instance contains a serial number (and presumably some other information that is not shown here). The Entity constructor (which is like an instance method) initializes the new instance with the next available serial number. Because the constructor is an instance member, it is permitted to access both the serialNo instance field and the nextSerialNo static field.

The GetNextSerialNo and SetNextSerialNo static methods can access the nextSerialNo static field, but it would be an error for them to directly access the serialNo instance field.

The following example shows the use of the Entity class.

using System;

class Test
{
static void Main() {
Entity.SetNextSerialNo(1000);

Entity e1 = new Entity();
Entity e2 = new Entity();

Console.WriteLine(e1.GetSerialNo()); // Outputs "1000"
Console.WriteLine(e2.GetSerialNo()); // Outputs "1001"
Console.WriteLine(Entity.GetNextSerialNo()); // Outputs "1002"
}
}

Note that the SetNextSerialNo and GetNextSerialNo static methods are invoked on the class, whereas the GetSerialNo instance method is invoked on instances of the class.

P.21(原文P.)
ERIC LIPPERT
這裡比較微妙的一點是覆寫的虛擬方法仍然是屬於引人它的那個類的方法,而不是屬於覆寫它的那個類的方法。有時候重載決策規則更傾向於繼承類型的成員而非基類成員,覆寫一個方法並不會"移動"它在繼承層次中的位置。

A subtle point here is that an overridden virtual method is still considered to be a method of the class that introduced it, and not a method of the class that overrides it. The overload resolution rules in some cases prefer members of more derived types to those in base types; overriding a method does not "move" where that method belongs in this hierarchy.

P.25(原文P.)
BRAD ABRAMS
The method overloading feature can be abused. Generally speaking, it is better to use method overloading only when all of the methods do semantically the same thing. The way many developers on the consuming end think about method overloading is that a single method takes a variety of arguments. In fact, changing the type of a local variable, parameter, or property could cause a different overload to be called. Developers certainly should not see side effects of the decision to use overloading. For users, however, it can be a surprise when methods with the same name do different things. For example, in the early days of the .NET Framework (before version 1 shipped), we had this set of overloads on the string class:

public class String {
public int IndexOf (string value); // Returns the index of value with this instance
public int IndexOf (char value); // Returns the index of value with this instance
public int IndexOf (char [] value); // Returns the first index of any of the
characters in value within the current instance
}

This last overload caused problems as it does a different thing. For example,

"Joshua,Hannah, Joseph" .IndexOf("Hannah"); // Returns 7

but

"Joshua,Hannah, Joseph" .IndexOf(new char [] {'H','a','n','n','a,'h;"); // Returns 3

In this case, it would be better to rename the overload that does something different.

public class String {
public int IndexOf (string value); // Returns the index of value within this
instance
public int IndexOf (char value); // Returns the index of value within this
instance
public int IndexOfAny(char [] value); // Returns the first index of any of the
characters in value within the current
instance
}

P.27(原文P.)
BRAD ABRAMS
構造函數應該是惰性的!構造函數的最佳實踐應是盡量完成較小的工作,即,只是簡單地獲取參數以備後用。

Constructors should be lazy! The best practice is to do minimal work in the constructor—that is, to simply capture the arguments for later use. For example, you might capture the name of the file or the path to the database, but don't open those external resources until absolutely necessary. This practice helps to ensure that possibly scarce resources are allocated for the smallest amount of time possible.

P.27(原文P.)
ERIC LIPPERT
A standard "best practice" is to always expose field-like data as properties with getters and setters rather than exposing the field. That way, if you ever want to add functionality to your getter and setter (e.g., logging, data binding, security checking, and so on), you can easily do so without "breaking" any consumer of the code that might rely on the field always being there.

雖然從某種意義上來說這種手法有點違背另一條建議("避免過早的通用化"),但是這個新的"自動實現屬性"的特性對於一個類型的公共接口部分來說還是非常簡單和自然的。

Although in some sense this practice is a violation of another bit of good advice ("Avoid premature generalization"), the new "automatically implemented properties" feature makes it very easy and natural to use properties rather than fields as part of the public interface of a type.

P.28(原文P.)
JESSE LIBERTY
事實上,event只是一個關鍵字,用來告知C#要限定委托使用的方法,進而防止客戶直接調用一個事件或是通過賦值而不是添加一個句柄來劫持一個事件。

In truth, event is just a keyword that signals C# to restrict the way a delegate can be used, thereby preventing a client from directly invoking an event or hijacking an event by assigning a handler rather than adding a handler. The keyword event makes delegates behave in the way you expect events to behave!

P.30(原文P.)

using System;

class Test
{
static void Main() {
List a = new List();
a.Add(1);
a.Add(2);
List b = new List();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b); // Outputs "True"
b.Add(3);
Console.WriteLine(a == b); // Outputs "False"

}
}

第一個Console.WriteLine輸出的是True,因為兩個例表包含了數目相同的對象,它們的值和順序也都完全一樣。

The first Console.WriteLine outputs TRue because the two lists contain the same number of objects with the same values in the same order. Had List not defined operator ==, the first Console.WriteLine would have output False because a and b reference different List instances.

P.31(原文P.)
ERIC LIPPERT

The takeaway message here is that certain specific data-intensive applications, which would otherwise be gated on heap allocation performance, benefit greatly from using structs. The takeaway message is emphatically not "always use structs because they make your program faster."

The performance benefit here is a tradeoff: Structs can take less time to allocate, but because every assignment of a struct is a value copy, they can take more time to copy than a reference copy would take.

記住,優化任何不是最慢的部分都是沒有意義的。

Always remember that it makes little sense to optimize anything other than the slowest thing. If your program is not gated on heap allocations, then pondering whether to use structs or classes for performance reasons is not an effective use of your time. Find the slowest thing, and then optimize it.

P.31-32(原文P.)
而對於結構來說,每個變量都有自已的數據拷貝,所以對一個變量的操作絕對不可能會影響另一個變量。例如,下面的代碼段的輸出就取決於Point是類還是結構。

With classes, it is possible for two variables to reference the same object and, therefore, possible for operations on one variable to affect the object referenced by the other variable. With structs, each variable has its own copy of the data, and it is not possible for operations on one variable to affect the other. For example, the output produced by the following code fragment depends on whether Point is a class or a struct.

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

如果Point是一個類,那麼輸出就是20,因為a和b的引用的是同一個對象。如果Point是一個結構的話,輸出則為10,因為把a賦值給b的操作實際上是創建了一份值的拷貝,對a.x的賦值不會影響到這份拷貝。

If Point is a class, the output is 20 because a and b reference the same object. If Point is a struct, the output is 10 because the assignment of a to b creates a copy of the value, and this copy is unaffected by the subsequent assignment to a.x.

P.33(原文P.)
BILL WAGNER
FxCop規則不推薦使用多維數組

An FxCop rule recommends against multi-dimensional arrays; it's primarily guidance against using multi-dimensional arrays as sparse arrays. If you know that you really are filling in all the elements in the array, multi-dimensional arrays are fine.

P.33-34(原文P.)
ERIC LIPPERT

In a number of places thus far, the specification notes that a particular local initialization is equivalent to "assign something to a temporary variable, do something to the temporary variable, declare a local variable, and assign the temporary to the local variable." You may be wondering why the specification calls out this seemingly unnecessary indirection. Why not simply say that this is equivalent to

int[] a = new int[3];
a[0] = 1; a[1] = 2; a[2] =3;

這一步驟的必要產在於賦值分析的確定性。

This practice is necessary because of definite assignment analysis. We would like to ensure that all local variables are definitely assigned before they are used. In particular, we would like an expression such as object[] arr = {arr}; to be illegal because it appears to use arr before it is definitely assigned. If this were equivalent to

object[] arr = new object[1];
arr[0] = arr;


then that would be legal. But by saying that this expression is equivalent to

object[] temp = new object[1];
temp[0] = arr;
object[] arr = temp;

then it becomes clear that arr is being used before it is assigned.

P.36-37(原文P.)
Enum values can be converted to integral values, and vice versa, using type casts. For example,

int i = (int)Color.Blue; // int i = 2;
Color c = (Color)2; // Color c = Color.Blue;

任何枚舉的預設值都是由整數值0轉換而來。
Bear: 這裡似乎也有翻錯,整型值0應該是整數值0才對。

The default value of any enum type is the integral value zero converted to the enum type. In cases where variables are automatically initialized to a default value, this is the value given to variables of enum types. For the default value of an enum type to be easily available, the literal 0 implicitly converts to any enum type. Thus, the following expression is permitted:

Color c = 0;

P.37(原文P.)
BILL WAGNER
這裡的潛台詞就是你應該讓任何列舉類型都接受0為一個合法的成員。

This rule implies that you should always ensure that 0 is a valid member of any enum type.

P.50(原文P.)
ERIC LIPPERT
C#自第一版發布以來沒有添加任何新的保留關鍵字。所有要求新關鍵字(yield, select等)的語言新特性都採用了"上下文關鍵字"的手法,也就是它們並非保留字,只是在特定的上下文里有特殊含義而已。這樣'就能保持向後兼容已有的程序了。

C# has added no new reserved keywords since its original release. All the new language features that require new keywords (yield, select, and so on) use "contextual keywords," which are not reserved and have special meaning only in context. This decision helps preserve backward compatibility with existing programs.

P.52(原文P.)
JOSEPH ALBAHARI
Of all the numeric suffixes, m and f are by far the most useful. Without these suffixes, a fractional float or decimal literal cannot be specified without a cast. For example, the following code will not compile, because the literal 1.5 will be parsed as type double:

float x = 1.5; // Error: no implicit conversion from double to float
decimal y = 1.5; // Error: no implicit conversion from double to decimal

Interestingly, the following code does compile, because C# defines an implicit conversion from int to decimal:

decimal z = 123; // Okay: parsed as int, and then implicitly converted to
decimal

技術上來說,後綴d是冗餘的,因為小數點就已經足以說明其類型了。

The d suffix is technically redundant in that the presence of a decimal point does the same job:

Console.WriteLine ((123.0).GetType() == typeof (double)); // True

P.55(原文P.)

Bear: 這段話不論是簡體版還是英文版Bear就是看不懂,有沒有人看的懂的可以解釋一下的?

Because a hexadecimal escape sequence can have a variable number of hexadecimal digits, the string literal "\x123" contains a single character with hexadecimal value 123. To create a string containing the character with hexadecimal value 12 followed by the character 3, one could write "\x00123" or "\x12" + "3" instead.

P.55(原文P.)
class Test
{

static void Main() {
object a = "hello";
object b = "hello";
System.Console.WriteLine(a == b);

}

}

is true because the two literals refer to the same string instance.

P.72(原文P.)
Protected internal (meaning protected or internal), which is selected by including both a protected modifier and an internal modifier in the member declaration. The intuitive meaning of protected internal is "access limited to this program or types derived from the containing class."

P.73(原文P.)
ERIC LIPPERT

"聲明"可訪問產和實際可訪問性的效果是有區別的。例如,一個在聲明為internal的類別裡聲明為public的方法,實際上就是一個internal方法。

There is a difference between the "declared" accessibility and the actual effective accessibility. For example, a method declared as public on a class declared as internal is, for most practical purposes, an internal method.

A good way to think about this issue is to recognize that a public class member is public only to the entities that have access to the class.

P.75(原文P.)
JOSEPH ALBAHARI

在一個internal類型裡聲明公有成員看起來毫無意義,因為這個成員的可見性會被限制在internal之內。但是如果這個公有成員修飾符被翻釋為"和所屬類型擁有相同可見性"的話,這麼做還是有意義的。

Declaring a public member within an internal type might seem pointless, given that the member's visibility will be capped at internal. It can make sense, however, if the public member modifier is interpreted as meaning "having the same visibility as the containing type."

在決定是把一個internal類型的成員聲明為public還是internal的時候,試著問這樣的問題:"如果稍後這個類型被提昇為public,我是不是希望這個成員也變成public? "如果答案是肯定的,你就可以在一開始把這個成員聲明為public。

A good question to ask, in deciding whether to declare a member of an internal type public or internal, is this: "If the type was later promoted to public, would I want this member to become public, too?" If the answer is yes, one could argue for declaring the member as public from the outset.

P.84(原文P.)
CHRIS SELLS

如果你想要依靠使用new來隱藏基類的實例方法,那你一定會失望的。因為調用者可以簡單地轉換到基類來獲得"被隱藏"的方法。

If you find yourself using new to hide an instance method on the base class, you're almost always going to be disappointed, if for no other reason than a caller can simply cast to the base class to get to the "hidden" method. For example,

class Base { public void F() {} }
class Derived : Base { new public void F() {} }
Derived d = new Derived();
((Base)d).F(); // Base.F not so hidden as you'd like...

You'll be much happier if you pick a new name for the method in the derived class instead.