.NET新平臺程式設計 (轉)
.NET新平臺程式設計 (轉)[@more@]新平臺
Jeffrey Richter
For the past year or so, I've been focusing my attention on the ? common language runtime platform. In my opinion, most new development will target this platfobecause it makes application development so much easier and faster. I also expect existing application development to move toward .NET at a rd pace.
在過去的一年裡,我一直注意的.NET通用語言執行庫平臺。在我看來,大多數新的開發將轉到這個平臺,因為他將使開發變得非常簡單而。我也希望現有的開發也儘快轉移到.NET。
To help developers embrace this new platform, my next few columns will focus on various programming issues specific to .NET. I'll assume that you are already familiar with -oriented programming concepts. Each column will focus on run-of-the-mill programming topics that are specific to the common language runtime. All .NET developers must become aware of these topics.
為了幫助開發者理解這個新平臺,我的下幾個專欄將著重講述各種.NET程式設計問題。我假定你已經熟悉面向程式設計概念。每個專欄將針對一般的通用語言執行庫的程式設計問題。所有的開發者必須瞭解這些問題。
When showing code examples, I had to choose one of the many languages that support the .NET common language runtime. The most neutral language to choose would have been intermediate language (IL) assembly. However, it is unlikely that IL assembly will be the most popular language, so I've decd to use . C# is the new language that Microsoft designed specifically for the development of managed code. While the sample code shown is C# and the explanations are geared toward coding with C#, the concepts discussed are those exposed by the common language runtime and therefore apply to any language that targets it.
在給出程式設計示例的時候,我需要從.NET通用語言執行庫支援的程式語言裡來選擇一種。最應該選擇的是中間語言。然而,遺憾的是它不是最流行的語言,所以我選擇C#。C#是一種新語言,微軟專門為了開發受控制程式碼而設計的。雖然示例是用C#的,並且解釋也是針對C#的,但討論的概念卻是適合所有使用通用語言執行庫的程式語言的。
My goal is to introduce various programming topics and to give you some idea of how they're implemented. It is not my goal to fully describe each topic and all the nuances that surround it. For complete details on any topic presented, please refer to the common language runtime or language documentation. Now, with the introduction out of the way, let's begin….
我的目的是介紹各種程式設計問題,並且給大家一些有關他們是如何實現的知識。
True Object-oriented Design
真正的物件導向設計
For programmers using the ? SDK, access to most of the operating system features is through a set of standalone functions exported from DLLs. These standalone functions are very easy to call from non-object-oriented languages like C. However, it is quite daunting for new developers to face literally thousands of independent functions that, on the surface, seem unrelated. Making things more difficult is the fact that many functions start with the Get (for example, GetCurrentProcess and GetStockObject). In addition, the Win32 API has evolved over the years and Microsoft has added new functions having similar semantics but offering slightly different features over the earlier functions. You can usually identify the newer functions because their names are similar to the original function's name (such as CreateWindow/CreateWindowEx, CreateTypeLib/CreateTypeLib2, and one of my personal favorites: CreatePen/CreatePenIndirect/ExtCreatePen).
對於使用 2000 SDK 的員,都是透過一系列獨立的從DLL引出的來訪問大多數操作特性的。這些孤立函式在非面嚮物件語言如C都可以很容易地。然而,令新的開發者比較頭疼的是要面對許多表面上看似獨立的不相干的函式。但實際上許多函式之間有複雜的關係,比如有很多函式以單詞Get(例如:GetCurrentProcess 和 GetStockObject)開頭。另外,Win32API發展了很多年了,微軟加了一些新函式,看上去名字很象但使用上同早期的函式有細微的差別。你可能經常會把一個新的函式看成和老的一樣僅因為名字很象(比如:CreateWindow/CreateWindowEx, CreateTypeLib / CreateTypeLib2, CreatePen / CreatePenIndirect / ExtCreatePen)。
All of these issues have given programmers the impression that develo for Windows? is difficult. With .NET, Microsoft is finally addressing developers' cries for help by creating an entirely object-oriented platform. Platform services are now divided into individual namespaces (such as System.Collections, System.Data, System.IO, System.Security, System., and so on), and each namespace contains a set of related class types that allow access to the platform's services.
所有的這些問題給從事Windows開發的人留下的深刻印象是難。使用.NET,微軟創造了一個完全的物件導向平臺,終於解決了困擾開發者的問題。平臺服務現在被分成了各個對立的Namespace(這個詞很難準確翻譯,還是用英文寫吧)(如:系統,集合,系統資料,系統IO,系統,系統Web,等等),並且每個Namespace包含一系列的相關類用來訪問平臺服務。
Since class methods may be overloaded, methods that differ just slightly in their behavior are given identical names and differ only by their s. For example, a class might offer three different versions of a CreatePen method. All methods do the same thing: create a pen. However, each method takes a different set of parameters and has slightly different behavior. In the future, should Microsoft need to create a fourth CreatePen method, the new method would fit seamley into the class as a first-class citizen.
因為類方法可能被過載,同名的方法的引數不同可能作用也有些不同。例如,一個類有三個不同的版本的CreatePen方法。所有的方法做同樣的事情,建立一個筆。然而,每個方法有不同的引數和行為上的細微差別。將來,微軟可能要建立第四個CreatePen方法,新方法將成為這個類的成員。
Since all platform services are offered via this object-oriented paradigm, software developers should have some understanding of object-oriented programming. The object-oriented paradigm enables other features as well. For example, it is now easy to create specialized versions of the base class library's types using inheritance and polymorphism. Again, I strongly suggest you become familiar with these concepts, as they are now critical to working with the Microsoft .NET .
因為所有的平臺服務都是透過物件導向方式提供的,開發者應該對物件導向有一些瞭解。物件導向有很多優點。例如,很容易使用繼承和多型建立一個基礎類庫的特別版本。我再次強烈建議熟悉這些概念,因為這些和微軟的.NET構架緊密相關。
System.Object
系統物件
In .NET, every object is ultimately derived from the System.Object type. This means that the following two type definitions (shown using C#) are identical:
在.NET,每個物件都從System.Object派生。著意味著下列兩個型別定義是一樣的。
class Jeff {
???
}
and
和
class Jeff : System.Object {
???
}
Since all object types are ultimately derived from System.Object, you are guaranteed that every object of every type has a minimum set of capabilities. The public methods available in the System.Object class are shown in Figure 1.
因為所有的物件型別都從System.Object派生,這樣保證了每個型別的物件有一系列最基本的功能。System.Object的公有方法如圖1
The common language runtime requires that all object instances be created using the new operator (which calls the newobj IL instruction). The following code shows how to create an instance of the Jeff type (declared previously):
通用語言執行庫要求所有的物件都用 new算符建立(呼叫newobj IL指令)。下面的程式碼演示如何建立一個Jeff型別的的例項。
Jeff j = new Jeff("ConstructorParam1");
The new operator creates the object by allocating the number of bytes required for the specified type from the managed heap. It initializes the object's overhead members. Every object has some additional bytes that the common language runtime uses to manage the object, such as the object's virtual table pointer and a reference to a sync block.
New算符從受控制的堆裡分配指定型別需要的來建立物件。它初始化物件成員。每個物件都有一些附加位元組,通用語言執行庫用來管理物件,如物件的虛表指標和同步鎖定引用。
The class type's constructor is called, passing it any parameters (the string "ConstructorParam1" in the earlier example) specified in the call to new. Note that most languages will compile constructors so that they call the base type's constructor; however, this is not required by the common language runtime.
建立新例項的時候,類的建構函式會被呼叫,還會給它一些引數。注意多數語言編譯的建構函式會呼叫基類的建構函式,然而,這不是通用語言執行庫所要求的。
After new has performed all of the operations I just mentioned, it returns a reference to the newly created object. In the example code, this reference is saved in the variable j, which is of type Jeff.
By the way, the new operator has no counterpart. That is, there is no way to explicitly free or destroy an object. The common language runtime offers a garbage-collected environment that automatically detects when objects are no longer being used or accessed and frees the objects automatically, which is a topic I plan to cover in an upcoming issue of MSDN? Magazine.
在new完成了我所說的所有操作之後,它返回一個新建立的物件的引用。在例子裡,引用被儲存到變數j裡,j的型別是Jeff。順便說一下,new算符沒有一個對應的算符,也就是說,沒有可以明確的釋放或登出一個物件的方法。通用語言執行庫提供一個垃圾回收環境,能自動檢測,如果物件不再使用或被訪問將會被自動刪除。這就是我將在接下來的MSDN雜誌裡討論的主題。
Casting Between Data Types
轉換資料型別
When programming, it is quite common to cast an object from one data type to another. In this section, I'll look at the rules that govern how objects are cast between data types. To start, look at the following line:
程式設計時經常要轉換物件的型別,在這一部分,我們來看看物件型別的規則。看看以下這行程式:
System.Object o = new Jeff("ConstructorParam1");
The previous line of code compiles and executes correctly because there is an implied cast. The new operator returns a reference to a Jeff type, but o is a reference to a System.Object type. Since all types (including the Jeff type) can be cast to System.Object, the implied cast is succesul.
However, if you execute the following line, you get a compiler error since the compiler does not provide an implicit cast from a base type to a derived type.
這行程式可以正確編譯執行,因為這是隱式型別轉換。New算符返回Jeff型別的引用,但o是一個System.Object型別的。因為所有的型別都從System.object派生,所以都可以轉換成System.Object型別。然而如果你以下這行,就會產生編譯錯誤,不能做從基類到派生類的隱式轉換。
Jeff j = o;
To get the command to compile, you must insert an explicit cast, as follows:
想要編譯透過,你必須加上強制轉換。
Jeff j = (Jeff) o;
Now the code compiles and executes successfully.
Let's look at another example:
現在程式碼可以編譯執行了,再看看另一個例子
System.Object o = new System.Object();
Jeff j = (Jeff) o;
On the first line, I have created an object of type System.Object. On the second line, I am attempting to convert a reference of type System.Object to a reference of type Jeff. Both lines of code compile just fine. However, when executed, the second line generates an InvalidCastException exception, which if not caught, forces the application to tenate.
When the second line of code executes, the common language runtime verifies that the object referred to by o is in fact an object of type Jeff (or any type derived from type Jeff). If so, the common language runtime allows the cast. However, if the object referenced by o has no relationshto Jeff, or is a base class of Jeff, then the common language runtime prevents the unsafe cast and raises the InvalidCastException exception.
在第一行,我建立一個物件,型別是System.Object,在第二行,我試圖把System.Object型別的引用轉換成Jeff型別的。兩行程式碼都可以正確編譯。然而,在執行時,第二行程式碼會產生一個InvalidCastException,如果沒有捕獲這個異常,那麼程式將終止。
當第二行程式碼執行時,通用語言執行庫驗證物件o引用的型別是否是一個Jeff型別,如果是,通用語言執行庫允許轉換。如果o引用的物件和Jeff沒有關係或者是Jeff的基類,那麼通用語言執行庫將禁止這種不安全的轉換,並且產生一個異常。
C# offers another way to perform a cast using the as operator:
Jeff j = new Jeff(); // Create a new Jeff object建立一個新的Jeff物件
System.Object o = j as System.Object; // 轉換成Object
// o now refers to the Jeff object o現在引用一個Jeff物件
The as operator attempts to cast an object to the specified type. However, unlike normal casting, the as operator will never throw an exception. Instead, if the object's type cannot be cast successfully, then the result is null. When the ill-cast reference is used, a NullReferenceException exception will be thrown. The following code demonstrates this concept.
As算符用來嘗試把物件轉換成指定型別。然而,不同於普通的轉換,as算符不會丟擲異常。而且,如果物件的型別不能成功轉換將返回NULL。當使用錯誤的引用時,將丟擲NullReferenceException這個異常。下面的程式碼演示了這個概念。
System.Object o = new System.Object(); //Creates a new Object object
Jeff j = o as Jeff; //Casts o to a Jeff
// The cast above fails: no exception is raised but j is set to null
j.ToString(); // Accessing j generates a NullReferenceException
In addition to the as operator, C# also offers an is operator. The is operator checks whether an object instance is compatible with a given type and the result of the evaluation is either True or False. The is operator will never raise an exception.
為了配合as算符,C#還提供一個is算符。Is算符檢查一個物件是否是某個型別的,返回值是TRUE或者FALSE。Is算符不會產生異常。
System.Object o = new System.Object();
System.Boolean b1 = (o is System.Object); // b1 is True
System.Boolean b2 = (o is Jeff); // b2 is False
Note, if the object reference is null, the is operator always returns False since there is no object available to check its type.
To make sure you understand everything just presented, assume that the following two class definitions exist.
注意,如果物件引用為NULL,is算符回返回false,因為沒有物件可供檢查型別。
class B {
int x;
}
class D : B {
int x;
}
Now, check Figure 2 to see which lines would compile and execute successfully (ES), which would cause a compiler error (CE), and which would cause a common language runtime error (RE).
請看圖2,哪一行將正確編譯執行,哪一行將產生編譯錯誤,哪一行將產生執行錯誤。
Assemblies and Namespaces
A collection of types can be grouped together into an assembly (a set of one or more files) and deployed. Within an assembly, individual namespaces can exist. To the application developer, namespaces look like logical groupings of related types. For example, the base class library assembly contains many namespaces. The System namespace includes core low-level types such as the Object base type, Byte, Int32, Exception, Math, and Delegate, while the System.Collections namespace includes such types as ArrayList, BitArray, Queue, and Stack.
集合和Namespaces
同一類的型別可以被組合在一起形成一個集合。在集合裡,允許存在獨立的namespace。對於開發者,namespace就象一些相關類的邏輯上的組。例如,基礎類庫集合包括很多namespace。System namespace核心的低階型別如Object基類,Byte,Int32,Exception, Math, and Delegate,而System.Collections namespace包括這些型別ArrayList, BitArray, Queue, and Stack.。
To the compiler, a namespace is simply an easy way of making a type's name longer and ensuring uniqueness by preceding the name with some symbols separated by dots. To the compiler, the Object type in the System namespace really just identifies a type called System.Object. Similarly, the Queue type in the System.Collections namespace simply identifies a type called System.Collections.Queue.
對於,namespace是保證型別的名字唯一的一種簡單的方法,不同的名字和符號靠點來分開。一個Object類在System Namespace裡可以寫做System.Object。同樣地,Queue類在System.Collections namespace裡可以寫做System.Collections.Queue。
The runtime engine doesn't know anything about namespaces. When you access a type, the common language runtime just needs to know the full name of the type and which assembly contains the definition of the type so that the common language runtime can load the correct assembly, find the type, and manipulate it.
執行引擎並不知道有namespace,當你訪問一個型別,通用語言執行庫僅需要知道型別的全稱和在哪個集合裡有這個型別的定義,以便通用語言執行庫可以正確載入查詢和操作。
Programmers usually want the most concise way of expressing their algorithms; it is extremely inconvenient to refer to every class type using its fully qualified name. For this reason, many programming languages offer a statement that instructs the compiler to try appending various prefixes to a type name until a match is made. When coding in C#, I usually place the following line at the top of my code modules:
程式設計師通常希望以最簡單的方法表達算式,這樣用全稱引用類就比較困難。因此,許多程式語言提供一種指令,可以讓編譯器來新增字首。在用C#時,我通常在程式的開頭加上下面這段語句。
using System;
When I refer to a type in my code, the compiler needs to ensure that the type is defined and that my code accesses the type in the correct way. If the compiler can't find a type with the specified name, it tries appending "System." to the type name and checks if the generated name matches an existing type. The previous line of code allows me to use Object in my code, and the compiler will automatically expand the name to System.Object. I'm sure you can easily imagine how much typing this saves.
當我在程式碼裡引用某個型別時,編譯器需要得到這個型別的定義並且保證我的程式碼可以正確訪問這個型別。如果編譯器用名字不能找到型別的定義,它會嘗試加上”system”字首再查詢。前面這行程式碼使我可以訪問Object類,編譯器自動把名字變成System.Object。可想而知這樣會少打很多字。
When checking for a type's definition, the compiler must know which assembly contains the type so that the assembly information and the type information can be emitted into the resulting file. To get the assembly information, you must pass the assembly that defines any referenced types to the compiler.
當檢查型別定義時,編譯器需要知道哪個集合包含型別定義資訊,以便把它加到結果裡。為了取得集合資訊,你要把包含引用型別定義的集合給編譯器。
As you might imagine, there are some potential problems with this scheme. For programming convenience, you should avoid creating types that have conflicting names. However, in some cases, it is simply not possible. .NET encourages the reuse of components. Your application may take advantage of a component created by Microsoft and another component created by me. Both of these companies' components may offer a type called FooBarMicrosoft's FooBar does one thing and Richter's FooBar does something entirely different. In this scenario, you had no control over the naming of the class types. To reference the Microsoft FooBar, you'd use Microsoft.FooBar and to reference my FooBar, you'd use Richter.FooBar.
可以想象,這樣做會有一些潛在的問題。為了程式設計方便,你應該儘量避免使用可能造成衝突的名字。然而,某些情況下,簡直不可能。.NET主張重用。你的程式可能用到微軟的元件和我的另一個元件。兩個元件都叫FootBar,但是兩個元件的功能完全不同。這種情況下你不能僅透過型別名來控制物件,引用微軟的FootBar你要用Microsoft.FootBar,引用我的FootBar要用Richter.FootBar。
In the following code, the reference to FooBar is ambiguous. It might be nice if the compiler reported an error here, but the C# compiler actually just picks one of the possible FooBar types; you won't diver a problem until runtime:
下面的程式碼裡,FootBar的引用是不明確的。編譯器在這裡最好能保錯,但是C#編譯器會選一個可用的型別,知道執行時你才會發現有問題。
using Microsoft;
using Richter;
class MyApp {
method void Hi() {
FooBar f = new FooBar(); // Ambiguous, compiler picks
}
}
To remove the ambiguity, you must explicitly tell the compiler which FooBar you want to create.
為了去掉不明確的引用,你必須明確地告訴編譯器你到底要建立哪個FootBar。
using Microsoft;
using Richter;
class MyApp {
method void Hi() {
Richter.FooBar f = new Richter.FooBar(); // Not ambiguous
}
}
There is another form of the using statement that allows you to create an alias for a single type. This is handy if you have just a few types that you use from a namespace and don't want to pollute the global namespace with all of a namespace's types. The following code demonstrates another way to solve the ambiguity problem.
另一種方式是為簡單型別建立一個別名。如果你只從某個namespace裡引用少數幾個型別,而又不想影響所有的namespace型別,這樣比較簡單。下面的程式碼演示如何解決不明確引用的問題。
// Define RichterFooBar symbol as an alias to Richter.FooBar
using RichterFooBar = Richter.FooBar;
class MyApp {
method void Hi() {
RichterFooBar f = new RichterFooBar(); // No error now
}
}
These methods of disambiguating a type are useful, but there are scenariwhere this is still not good enough. Imagine that the Australian Boomerang Company (ABC) and the Alaskan Boat Corporation (ABC) are each creating a type, called BuyProduct, which they intend to ship in their respective assemblies. It is likely that both companies would create a namespace called ABC that contains a type called BuyProduct. Anyone who tries to develop an application that needs to buy both boomerangs and boats would be in for some trouble unless your programming language gives you a way to programmatically distinguish between the assembliesnot just between namespaces.
這種消除歧義的方法很有用,但還不夠好。假如Australian Boomerang Company (ABC) and the Alaskan Boat Corporation (ABC)分別建立一個型別叫BuyProduct,並打算放在各自的集合裡。兩個公司都將建立包含BuyProduct型別的ABC namespace。如果某個開發者需要購買boomerangs 和boats就會產生麻煩,除非你的程式語言提供程式的方法來區別兩個集合。
Unfortunately, the C# using statement only supports namespaces and does not offer any way to specify an assembly. However, in the real world, this problem does not come up very often, so it's rarely an issue. If you are designing component types that you expect third parties to use, it is highly recommended that you define these types in a namespace so compilers can easily disambiguate types. In fact, you should use your full company name (not an acronym) to be your top-level namespace name to reduce the likelihood of conflict. You can see that Microsoft uses a namespace of "Microsoft".
不幸的是,C#指令僅支援Namespace而不提供區別集合的方法。然而在現實世界裡這個問題不經常發生。但它的確是個問題。如果你設計的元件希望給第三方使用,推薦你使用namespace來定義型別,以便編譯器可以區分型別。實際上,你應該用公司全名做頂級namespace來減少衝突的可能性。微軟使用”Microsoft”
Creating a namespace is simply a matter of writing a namespace declaration into your code, as follows:
建立namespce和簡單,如下所示來寫namespace的宣告。
namespace CompanyName { // CompanyName
class A { // CompanyName.A
class B { ... } // CompanyName.A.B
}
namespace X { // CompanyName.X
class C { ... } // CompanyName.X.C
}
}
Note that namespaces are implicitly public. You cannot change this by including any access modifiers. However, you can define types within a namespace that are internal (can't be used outside the assembly) or public (can be accessed by any assembly). Namespaces denote logical containment only; accessibility or packaging is accomplished by placing the namespace into an assembly.
注意,namespace卻省是公有的。不能靠任何訪問修飾來修改。你可以在namespace裡來定義內部的和外部的型別,namespace表示邏輯上的包含。
In my next column, I'll describe the different kinds of types that all .NET programmers must be aware of: primitive types, reference types, and value types. A good understanding of value types is extremely important to every .NET programmer.
下一個專欄,我會講述不同型別的.Net開發者都必須瞭解的原始型別,引用型別和值型別。透徹理解值型別對每個.NET開發者尤其重要。
For the past year or so, I've been focusing my attention on the ? common language runtime platform. In my opinion, most new development will target this platfobecause it makes application development so much easier and faster. I also expect existing application development to move toward .NET at a rd pace.
在過去的一年裡,我一直注意的.NET通用語言執行庫平臺。在我看來,大多數新的開發將轉到這個平臺,因為他將使開發變得非常簡單而。我也希望現有的開發也儘快轉移到.NET。
To help developers embrace this new platform, my next few columns will focus on various programming issues specific to .NET. I'll assume that you are already familiar with -oriented programming concepts. Each column will focus on run-of-the-mill programming topics that are specific to the common language runtime. All .NET developers must become aware of these topics.
為了幫助開發者理解這個新平臺,我的下幾個專欄將著重講述各種.NET程式設計問題。我假定你已經熟悉面向程式設計概念。每個專欄將針對一般的通用語言執行庫的程式設計問題。所有的開發者必須瞭解這些問題。
When showing code examples, I had to choose one of the many languages that support the .NET common language runtime. The most neutral language to choose would have been intermediate language (IL) assembly. However, it is unlikely that IL assembly will be the most popular language, so I've decd to use . C# is the new language that Microsoft designed specifically for the development of managed code. While the sample code shown is C# and the explanations are geared toward coding with C#, the concepts discussed are those exposed by the common language runtime and therefore apply to any language that targets it.
在給出程式設計示例的時候,我需要從.NET通用語言執行庫支援的程式語言裡來選擇一種。最應該選擇的是中間語言。然而,遺憾的是它不是最流行的語言,所以我選擇C#。C#是一種新語言,微軟專門為了開發受控制程式碼而設計的。雖然示例是用C#的,並且解釋也是針對C#的,但討論的概念卻是適合所有使用通用語言執行庫的程式語言的。
My goal is to introduce various programming topics and to give you some idea of how they're implemented. It is not my goal to fully describe each topic and all the nuances that surround it. For complete details on any topic presented, please refer to the common language runtime or language documentation. Now, with the introduction out of the way, let's begin….
我的目的是介紹各種程式設計問題,並且給大家一些有關他們是如何實現的知識。
True Object-oriented Design
真正的物件導向設計
For programmers using the ? SDK, access to most of the operating system features is through a set of standalone functions exported from DLLs. These standalone functions are very easy to call from non-object-oriented languages like C. However, it is quite daunting for new developers to face literally thousands of independent functions that, on the surface, seem unrelated. Making things more difficult is the fact that many functions start with the Get (for example, GetCurrentProcess and GetStockObject). In addition, the Win32 API has evolved over the years and Microsoft has added new functions having similar semantics but offering slightly different features over the earlier functions. You can usually identify the newer functions because their names are similar to the original function's name (such as CreateWindow/CreateWindowEx, CreateTypeLib/CreateTypeLib2, and one of my personal favorites: CreatePen/CreatePenIndirect/ExtCreatePen).
對於使用 2000 SDK 的員,都是透過一系列獨立的從DLL引出的來訪問大多數操作特性的。這些孤立函式在非面嚮物件語言如C都可以很容易地。然而,令新的開發者比較頭疼的是要面對許多表面上看似獨立的不相干的函式。但實際上許多函式之間有複雜的關係,比如有很多函式以單詞Get(例如:GetCurrentProcess 和 GetStockObject)開頭。另外,Win32API發展了很多年了,微軟加了一些新函式,看上去名字很象但使用上同早期的函式有細微的差別。你可能經常會把一個新的函式看成和老的一樣僅因為名字很象(比如:CreateWindow/CreateWindowEx, CreateTypeLib / CreateTypeLib2, CreatePen / CreatePenIndirect / ExtCreatePen)。
All of these issues have given programmers the impression that develo for Windows? is difficult. With .NET, Microsoft is finally addressing developers' cries for help by creating an entirely object-oriented platform. Platform services are now divided into individual namespaces (such as System.Collections, System.Data, System.IO, System.Security, System., and so on), and each namespace contains a set of related class types that allow access to the platform's services.
所有的這些問題給從事Windows開發的人留下的深刻印象是難。使用.NET,微軟創造了一個完全的物件導向平臺,終於解決了困擾開發者的問題。平臺服務現在被分成了各個對立的Namespace(這個詞很難準確翻譯,還是用英文寫吧)(如:系統,集合,系統資料,系統IO,系統,系統Web,等等),並且每個Namespace包含一系列的相關類用來訪問平臺服務。
Since class methods may be overloaded, methods that differ just slightly in their behavior are given identical names and differ only by their s. For example, a class might offer three different versions of a CreatePen method. All methods do the same thing: create a pen. However, each method takes a different set of parameters and has slightly different behavior. In the future, should Microsoft need to create a fourth CreatePen method, the new method would fit seamley into the class as a first-class citizen.
因為類方法可能被過載,同名的方法的引數不同可能作用也有些不同。例如,一個類有三個不同的版本的CreatePen方法。所有的方法做同樣的事情,建立一個筆。然而,每個方法有不同的引數和行為上的細微差別。將來,微軟可能要建立第四個CreatePen方法,新方法將成為這個類的成員。
Since all platform services are offered via this object-oriented paradigm, software developers should have some understanding of object-oriented programming. The object-oriented paradigm enables other features as well. For example, it is now easy to create specialized versions of the base class library's types using inheritance and polymorphism. Again, I strongly suggest you become familiar with these concepts, as they are now critical to working with the Microsoft .NET .
因為所有的平臺服務都是透過物件導向方式提供的,開發者應該對物件導向有一些瞭解。物件導向有很多優點。例如,很容易使用繼承和多型建立一個基礎類庫的特別版本。我再次強烈建議熟悉這些概念,因為這些和微軟的.NET構架緊密相關。
System.Object
系統物件
In .NET, every object is ultimately derived from the System.Object type. This means that the following two type definitions (shown using C#) are identical:
在.NET,每個物件都從System.Object派生。著意味著下列兩個型別定義是一樣的。
class Jeff {
???
}
and
和
class Jeff : System.Object {
???
}
Since all object types are ultimately derived from System.Object, you are guaranteed that every object of every type has a minimum set of capabilities. The public methods available in the System.Object class are shown in Figure 1.
因為所有的物件型別都從System.Object派生,這樣保證了每個型別的物件有一系列最基本的功能。System.Object的公有方法如圖1
The common language runtime requires that all object instances be created using the new operator (which calls the newobj IL instruction). The following code shows how to create an instance of the Jeff type (declared previously):
通用語言執行庫要求所有的物件都用 new算符建立(呼叫newobj IL指令)。下面的程式碼演示如何建立一個Jeff型別的的例項。
Jeff j = new Jeff("ConstructorParam1");
The new operator creates the object by allocating the number of bytes required for the specified type from the managed heap. It initializes the object's overhead members. Every object has some additional bytes that the common language runtime uses to manage the object, such as the object's virtual table pointer and a reference to a sync block.
New算符從受控制的堆裡分配指定型別需要的來建立物件。它初始化物件成員。每個物件都有一些附加位元組,通用語言執行庫用來管理物件,如物件的虛表指標和同步鎖定引用。
The class type's constructor is called, passing it any parameters (the string "ConstructorParam1" in the earlier example) specified in the call to new. Note that most languages will compile constructors so that they call the base type's constructor; however, this is not required by the common language runtime.
建立新例項的時候,類的建構函式會被呼叫,還會給它一些引數。注意多數語言編譯的建構函式會呼叫基類的建構函式,然而,這不是通用語言執行庫所要求的。
After new has performed all of the operations I just mentioned, it returns a reference to the newly created object. In the example code, this reference is saved in the variable j, which is of type Jeff.
By the way, the new operator has no counterpart. That is, there is no way to explicitly free or destroy an object. The common language runtime offers a garbage-collected environment that automatically detects when objects are no longer being used or accessed and frees the objects automatically, which is a topic I plan to cover in an upcoming issue of MSDN? Magazine.
在new完成了我所說的所有操作之後,它返回一個新建立的物件的引用。在例子裡,引用被儲存到變數j裡,j的型別是Jeff。順便說一下,new算符沒有一個對應的算符,也就是說,沒有可以明確的釋放或登出一個物件的方法。通用語言執行庫提供一個垃圾回收環境,能自動檢測,如果物件不再使用或被訪問將會被自動刪除。這就是我將在接下來的MSDN雜誌裡討論的主題。
Casting Between Data Types
轉換資料型別
When programming, it is quite common to cast an object from one data type to another. In this section, I'll look at the rules that govern how objects are cast between data types. To start, look at the following line:
程式設計時經常要轉換物件的型別,在這一部分,我們來看看物件型別的規則。看看以下這行程式:
System.Object o = new Jeff("ConstructorParam1");
The previous line of code compiles and executes correctly because there is an implied cast. The new operator returns a reference to a Jeff type, but o is a reference to a System.Object type. Since all types (including the Jeff type) can be cast to System.Object, the implied cast is succesul.
However, if you execute the following line, you get a compiler error since the compiler does not provide an implicit cast from a base type to a derived type.
這行程式可以正確編譯執行,因為這是隱式型別轉換。New算符返回Jeff型別的引用,但o是一個System.Object型別的。因為所有的型別都從System.object派生,所以都可以轉換成System.Object型別。然而如果你以下這行,就會產生編譯錯誤,不能做從基類到派生類的隱式轉換。
Jeff j = o;
To get the command to compile, you must insert an explicit cast, as follows:
想要編譯透過,你必須加上強制轉換。
Jeff j = (Jeff) o;
Now the code compiles and executes successfully.
Let's look at another example:
現在程式碼可以編譯執行了,再看看另一個例子
System.Object o = new System.Object();
Jeff j = (Jeff) o;
On the first line, I have created an object of type System.Object. On the second line, I am attempting to convert a reference of type System.Object to a reference of type Jeff. Both lines of code compile just fine. However, when executed, the second line generates an InvalidCastException exception, which if not caught, forces the application to tenate.
When the second line of code executes, the common language runtime verifies that the object referred to by o is in fact an object of type Jeff (or any type derived from type Jeff). If so, the common language runtime allows the cast. However, if the object referenced by o has no relationshto Jeff, or is a base class of Jeff, then the common language runtime prevents the unsafe cast and raises the InvalidCastException exception.
在第一行,我建立一個物件,型別是System.Object,在第二行,我試圖把System.Object型別的引用轉換成Jeff型別的。兩行程式碼都可以正確編譯。然而,在執行時,第二行程式碼會產生一個InvalidCastException,如果沒有捕獲這個異常,那麼程式將終止。
當第二行程式碼執行時,通用語言執行庫驗證物件o引用的型別是否是一個Jeff型別,如果是,通用語言執行庫允許轉換。如果o引用的物件和Jeff沒有關係或者是Jeff的基類,那麼通用語言執行庫將禁止這種不安全的轉換,並且產生一個異常。
C# offers another way to perform a cast using the as operator:
Jeff j = new Jeff(); // Create a new Jeff object建立一個新的Jeff物件
System.Object o = j as System.Object; // 轉換成Object
// o now refers to the Jeff object o現在引用一個Jeff物件
The as operator attempts to cast an object to the specified type. However, unlike normal casting, the as operator will never throw an exception. Instead, if the object's type cannot be cast successfully, then the result is null. When the ill-cast reference is used, a NullReferenceException exception will be thrown. The following code demonstrates this concept.
As算符用來嘗試把物件轉換成指定型別。然而,不同於普通的轉換,as算符不會丟擲異常。而且,如果物件的型別不能成功轉換將返回NULL。當使用錯誤的引用時,將丟擲NullReferenceException這個異常。下面的程式碼演示了這個概念。
System.Object o = new System.Object(); //Creates a new Object object
Jeff j = o as Jeff; //Casts o to a Jeff
// The cast above fails: no exception is raised but j is set to null
j.ToString(); // Accessing j generates a NullReferenceException
In addition to the as operator, C# also offers an is operator. The is operator checks whether an object instance is compatible with a given type and the result of the evaluation is either True or False. The is operator will never raise an exception.
為了配合as算符,C#還提供一個is算符。Is算符檢查一個物件是否是某個型別的,返回值是TRUE或者FALSE。Is算符不會產生異常。
System.Object o = new System.Object();
System.Boolean b1 = (o is System.Object); // b1 is True
System.Boolean b2 = (o is Jeff); // b2 is False
Note, if the object reference is null, the is operator always returns False since there is no object available to check its type.
To make sure you understand everything just presented, assume that the following two class definitions exist.
注意,如果物件引用為NULL,is算符回返回false,因為沒有物件可供檢查型別。
class B {
int x;
}
class D : B {
int x;
}
Now, check Figure 2 to see which lines would compile and execute successfully (ES), which would cause a compiler error (CE), and which would cause a common language runtime error (RE).
請看圖2,哪一行將正確編譯執行,哪一行將產生編譯錯誤,哪一行將產生執行錯誤。
Assemblies and Namespaces
A collection of types can be grouped together into an assembly (a set of one or more files) and deployed. Within an assembly, individual namespaces can exist. To the application developer, namespaces look like logical groupings of related types. For example, the base class library assembly contains many namespaces. The System namespace includes core low-level types such as the Object base type, Byte, Int32, Exception, Math, and Delegate, while the System.Collections namespace includes such types as ArrayList, BitArray, Queue, and Stack.
集合和Namespaces
同一類的型別可以被組合在一起形成一個集合。在集合裡,允許存在獨立的namespace。對於開發者,namespace就象一些相關類的邏輯上的組。例如,基礎類庫集合包括很多namespace。System namespace核心的低階型別如Object基類,Byte,Int32,Exception, Math, and Delegate,而System.Collections namespace包括這些型別ArrayList, BitArray, Queue, and Stack.。
To the compiler, a namespace is simply an easy way of making a type's name longer and ensuring uniqueness by preceding the name with some symbols separated by dots. To the compiler, the Object type in the System namespace really just identifies a type called System.Object. Similarly, the Queue type in the System.Collections namespace simply identifies a type called System.Collections.Queue.
對於,namespace是保證型別的名字唯一的一種簡單的方法,不同的名字和符號靠點來分開。一個Object類在System Namespace裡可以寫做System.Object。同樣地,Queue類在System.Collections namespace裡可以寫做System.Collections.Queue。
The runtime engine doesn't know anything about namespaces. When you access a type, the common language runtime just needs to know the full name of the type and which assembly contains the definition of the type so that the common language runtime can load the correct assembly, find the type, and manipulate it.
執行引擎並不知道有namespace,當你訪問一個型別,通用語言執行庫僅需要知道型別的全稱和在哪個集合裡有這個型別的定義,以便通用語言執行庫可以正確載入查詢和操作。
Programmers usually want the most concise way of expressing their algorithms; it is extremely inconvenient to refer to every class type using its fully qualified name. For this reason, many programming languages offer a statement that instructs the compiler to try appending various prefixes to a type name until a match is made. When coding in C#, I usually place the following line at the top of my code modules:
程式設計師通常希望以最簡單的方法表達算式,這樣用全稱引用類就比較困難。因此,許多程式語言提供一種指令,可以讓編譯器來新增字首。在用C#時,我通常在程式的開頭加上下面這段語句。
using System;
When I refer to a type in my code, the compiler needs to ensure that the type is defined and that my code accesses the type in the correct way. If the compiler can't find a type with the specified name, it tries appending "System." to the type name and checks if the generated name matches an existing type. The previous line of code allows me to use Object in my code, and the compiler will automatically expand the name to System.Object. I'm sure you can easily imagine how much typing this saves.
當我在程式碼裡引用某個型別時,編譯器需要得到這個型別的定義並且保證我的程式碼可以正確訪問這個型別。如果編譯器用名字不能找到型別的定義,它會嘗試加上”system”字首再查詢。前面這行程式碼使我可以訪問Object類,編譯器自動把名字變成System.Object。可想而知這樣會少打很多字。
When checking for a type's definition, the compiler must know which assembly contains the type so that the assembly information and the type information can be emitted into the resulting file. To get the assembly information, you must pass the assembly that defines any referenced types to the compiler.
當檢查型別定義時,編譯器需要知道哪個集合包含型別定義資訊,以便把它加到結果裡。為了取得集合資訊,你要把包含引用型別定義的集合給編譯器。
As you might imagine, there are some potential problems with this scheme. For programming convenience, you should avoid creating types that have conflicting names. However, in some cases, it is simply not possible. .NET encourages the reuse of components. Your application may take advantage of a component created by Microsoft and another component created by me. Both of these companies' components may offer a type called FooBarMicrosoft's FooBar does one thing and Richter's FooBar does something entirely different. In this scenario, you had no control over the naming of the class types. To reference the Microsoft FooBar, you'd use Microsoft.FooBar and to reference my FooBar, you'd use Richter.FooBar.
可以想象,這樣做會有一些潛在的問題。為了程式設計方便,你應該儘量避免使用可能造成衝突的名字。然而,某些情況下,簡直不可能。.NET主張重用。你的程式可能用到微軟的元件和我的另一個元件。兩個元件都叫FootBar,但是兩個元件的功能完全不同。這種情況下你不能僅透過型別名來控制物件,引用微軟的FootBar你要用Microsoft.FootBar,引用我的FootBar要用Richter.FootBar。
In the following code, the reference to FooBar is ambiguous. It might be nice if the compiler reported an error here, but the C# compiler actually just picks one of the possible FooBar types; you won't diver a problem until runtime:
下面的程式碼裡,FootBar的引用是不明確的。編譯器在這裡最好能保錯,但是C#編譯器會選一個可用的型別,知道執行時你才會發現有問題。
using Microsoft;
using Richter;
class MyApp {
method void Hi() {
FooBar f = new FooBar(); // Ambiguous, compiler picks
}
}
To remove the ambiguity, you must explicitly tell the compiler which FooBar you want to create.
為了去掉不明確的引用,你必須明確地告訴編譯器你到底要建立哪個FootBar。
using Microsoft;
using Richter;
class MyApp {
method void Hi() {
Richter.FooBar f = new Richter.FooBar(); // Not ambiguous
}
}
There is another form of the using statement that allows you to create an alias for a single type. This is handy if you have just a few types that you use from a namespace and don't want to pollute the global namespace with all of a namespace's types. The following code demonstrates another way to solve the ambiguity problem.
另一種方式是為簡單型別建立一個別名。如果你只從某個namespace裡引用少數幾個型別,而又不想影響所有的namespace型別,這樣比較簡單。下面的程式碼演示如何解決不明確引用的問題。
// Define RichterFooBar symbol as an alias to Richter.FooBar
using RichterFooBar = Richter.FooBar;
class MyApp {
method void Hi() {
RichterFooBar f = new RichterFooBar(); // No error now
}
}
These methods of disambiguating a type are useful, but there are scenariwhere this is still not good enough. Imagine that the Australian Boomerang Company (ABC) and the Alaskan Boat Corporation (ABC) are each creating a type, called BuyProduct, which they intend to ship in their respective assemblies. It is likely that both companies would create a namespace called ABC that contains a type called BuyProduct. Anyone who tries to develop an application that needs to buy both boomerangs and boats would be in for some trouble unless your programming language gives you a way to programmatically distinguish between the assembliesnot just between namespaces.
這種消除歧義的方法很有用,但還不夠好。假如Australian Boomerang Company (ABC) and the Alaskan Boat Corporation (ABC)分別建立一個型別叫BuyProduct,並打算放在各自的集合裡。兩個公司都將建立包含BuyProduct型別的ABC namespace。如果某個開發者需要購買boomerangs 和boats就會產生麻煩,除非你的程式語言提供程式的方法來區別兩個集合。
Unfortunately, the C# using statement only supports namespaces and does not offer any way to specify an assembly. However, in the real world, this problem does not come up very often, so it's rarely an issue. If you are designing component types that you expect third parties to use, it is highly recommended that you define these types in a namespace so compilers can easily disambiguate types. In fact, you should use your full company name (not an acronym) to be your top-level namespace name to reduce the likelihood of conflict. You can see that Microsoft uses a namespace of "Microsoft".
不幸的是,C#指令僅支援Namespace而不提供區別集合的方法。然而在現實世界裡這個問題不經常發生。但它的確是個問題。如果你設計的元件希望給第三方使用,推薦你使用namespace來定義型別,以便編譯器可以區分型別。實際上,你應該用公司全名做頂級namespace來減少衝突的可能性。微軟使用”Microsoft”
Creating a namespace is simply a matter of writing a namespace declaration into your code, as follows:
建立namespce和簡單,如下所示來寫namespace的宣告。
namespace CompanyName { // CompanyName
class A { // CompanyName.A
class B { ... } // CompanyName.A.B
}
namespace X { // CompanyName.X
class C { ... } // CompanyName.X.C
}
}
Note that namespaces are implicitly public. You cannot change this by including any access modifiers. However, you can define types within a namespace that are internal (can't be used outside the assembly) or public (can be accessed by any assembly). Namespaces denote logical containment only; accessibility or packaging is accomplished by placing the namespace into an assembly.
注意,namespace卻省是公有的。不能靠任何訪問修飾來修改。你可以在namespace裡來定義內部的和外部的型別,namespace表示邏輯上的包含。
In my next column, I'll describe the different kinds of types that all .NET programmers must be aware of: primitive types, reference types, and value types. A good understanding of value types is extremely important to every .NET programmer.
下一個專欄,我會講述不同型別的.Net開發者都必須瞭解的原始型別,引用型別和值型別。透徹理解值型別對每個.NET開發者尤其重要。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-995717/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 商城版小程式打造流量聚合新平臺
- 使用net-snmp API程式設計(轉)API程式設計
- .NET泛型程式設計簡介 (轉)泛型程式設計
- 有感程式設計師(http://www.vchome.net) (轉)程式設計師HTTP
- 物件導向程式設計——VB.NET&C# 篇 (轉)物件程式設計C#
- Attribute在.NET程式設計的應用(六) (轉)程式設計
- 京東科技設計稿轉程式碼平臺介紹
- .NET多執行緒程式設計(4):執行緒池和非同步程式設計 (轉)執行緒程式設計非同步
- 共享辦公室出租,資源配置新平臺
- .NET跨平臺實踐:.NetCore、.Net5/6 Linux守護程式設計NetCoreLinux程式設計
- 多程式程式設計 (轉)程式設計
- 多程式程式設計(轉)程式設計
- 程式設計教育平臺程式設計
- ERP新平臺促管理軟體第三次革命(轉)
- Attribute在.net程式設計中的應用(一) (轉)程式設計
- Attribute 在.NET程式設計中的應用(二) (轉)程式設計
- Attribute在.NET程式設計中的應用(四) (轉)程式設計
- Attribute在.NET程式設計中的應用(五) (轉)程式設計
- VB.NET中的物件導向程式設計特徵 (轉)物件程式設計特徵
- Linux程式設計入門 - socket/inetd programming(轉)Linux程式設計
- XML 程式設計思想:查詢 XML 格式的 WordNet(轉)XML程式設計
- Attribute在.NET程式設計中的應用(三) (轉)程式設計
- .NET併發程式設計-函數語言程式設計程式設計函數
- Linux 程式設計之Shell程式設計(轉)Linux程式設計
- Pcap程式設計(轉)PCA程式設計
- shell程式設計(轉)程式設計
- 程式設計之路 (轉)程式設計
- 安全可靠的創新平臺Nimbus
- Jakarta EE:雲原生Java的新平臺Java
- Prompts Royale: 革新提示工程的創新平臺
- 高科技企業如何建設產供協同創新平臺
- Mono 計劃―Linux版.NET平臺 (轉)MonoLinux
- .NET之非同步程式設計非同步程式設計
- [.net 物件導向程式設計深入](9).NET Core 跨平臺開發環境搭建物件程式設計開發環境
- .NET的併發程式設計(TPL程式設計)是什麼?程式設計
- 一個即將轉向.NET程式設計師的無奈程式設計師
- .NET多執行緒程式設計(3):執行緒同步 (轉)執行緒程式設計
- 在ASP.NET中物件導向的程式設計思想 (轉)ASP.NET物件程式設計