.NET new platform programming
Jeffrey Richter
For the past year or so, I've been focusing my attention on the Microsoft? .NET common language runtime platform. In my opinion, most new development will target this platform because it makes application development so much easier and faster. I also expect EXISTING Application Development to Move Toward .NET AT A Rapid PACE.
In the past year, I have been paying attention to Microsoft's .NET universal language runtime platform. In my opinion, most new development will go to this platform because he will make the development very simple and fast. I also hope that the existing development is also transferred as soon as possible to .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 object-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.
In order to help developers understand this new platform, the next few columns will focus on various .NET programming issues. I assume that you have been familiar with object-oriented programming concept. Each column will be awarded programming issues for a general universal language run. All developers must understand these issues.
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 decided to use C #. 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.
When a programming example is given, I need to choose one from the programming language supported by .NET universal language runtime. The most selected is the intermediate assembly language. However, unfortunately it is not the most popular language, so I choose C #. C # is a new language, Microsoft is designed for developing controlled code. Although the example is a C #, and the interpretation is also for C #, the concept of discussion is suitable for all programming languages that use the general language runtime. 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 ....
My purpose is to introduce a variety of programming issues and give you some knowledge about how they achieve.
True Object-Oriented Design
Real object-oriented design
For programmers using the Win32? 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 word 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.
For programmers using Windows 2000 SDK, they are all accessed through a series of independent functions that are drawn from DLL. These isolated functions can be easily called in non-domain-oriented languages such as C. However, the new developer is more headache to face many surfaces that seem to be independent. But in fact, there is a complex relationship between many functions, such as many functions, with a word GET (for example, getCurrentProcess, and getStockObject). In addition, Win32API has developed many years, Microsoft adds some new functions, which looks very similar, but uses the same function in the same time. You may often see a new function as old as because the name is very like (such as: CreateWindow / CreateTYPELIB2, CREATEPEN / CREATEPENIRECT / EXTCREATEPEN). All of these issues have given programmers the impression that developing 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.Web, And So ON, AND Each Namespace Contains a set of research class type.
All of these issues is difficult to leave a deep impression that WINDOWS developed. Using .NET, Microsoft created a completely object-oriented platform, finally solved the problem of troubled developers. Platform service is now divided into each opposite Namespace (this word is difficult to translate, or write in English) (eg, system, collection, system data, system IO, system security, system web, etc.), and each Namespace contains a range of related classes to access platform services.
Since class methods may be overloaded, methods that differ just slightly in their behavior are given identical names and differ only by their prototypes 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 seamlessly into the class as a first-class citizen.
Because class methods may be overloaded, the same number of methods of the same name may be different. For example, a class has three different versions of Createpen methods. All methods do the same thing, create a pen. However, each method has different parameters and behaviors. In the future, Microsoft may have to create a fourth Createpen method, and a new method will become a member of this class. 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 concerts, as The Microsoft .NET Framework.
Because all platform services are provided by object-oriented methods, software developers should have some understanding of object-oriented objects. Object-oriented has many advantages. For example, it is easy to use inheritance and polymorphism to create a special version of a basic class library. I strongly recommend familiar with these concepts because they are closely related to Microsoft .NET architecture.
System.Object
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:
At .NET, each object is derived from System.Object. It means that the following two types definitions are the same.
Class Jeff {
???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
}
and
with
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.
Because all object types are derived from System.Object, so that each type of object has a series of basic functions. System.Object's public approach is shown in Figure 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).:
General Language Runture Requirements All objects are created with a New operator (call newobj IL instruction). The following code demonstrates how to create an instance of a JEFF type.
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.. .
The New operator assigns the memory required by the controlled pile to create objects. It initializes the object member. Each object has some additional bytes, universal language runtime uses to manage objects, such as object's virtual table pointer and synchronous lock reference.
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.
When you create a new instance, the constructor of the class will be called and some parameters will be given. Note that the constructor compiled multi-language compiles will call the constructor of the base class, however, this is not required by the General Language Runtime.
After New Has Performed All of the Operations I Just Mentioned, IT Returns A Reference. In 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 Automatic or Which Is A Topic I Plan To Cover In an Upcoming Issue of MSDN? Magazine.
After the New has completed all the operations I said, it returns a reference to a newly created object. In the example, the reference is saved to the variable j, the type J is Jeff. By the way, the New operator does not have a corresponding operator, that is, there is no way to release or cancel an object. A generic language runtime provides a garbage collection environment that automatically detects if the object is no longer used or accessed will be automatically deleted. This is the theme I will discuss in the next MSDN magazine.
Casting BetWeen Data Types Conversion Data Type
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..:
When programming, the type of object is often converted, in this section, let's take a look at the rules of the object type. Take a 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 Successful.
However, if you Execute The Following Line, You Get A Compiler Error Since The Compiler Do Provide An Implicit Cast from a base type to a derived type.
This line can be compiled correctly because it is implicit type conversion. The New operator returns a reference to the JEFF type, but o is a system.object type. Because all types are derived from System.Object, they can be converted to the System.Object type. However, if you do this, you will generate compilation errors and cannot do implicit conversions from base classes to derived classes.
Jeff j = O;
To Get The Command To Compile, You Must Insert An Explicit Cast, As Follows:
If you want to compile, you have to add forced conversion.
Jeff J = (Jeff) O;
Now the code compiles and executes successfully.
Let's Look at another example:
Now the code can be compiled, and then 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 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 relationship to Jeff, or is a base class of Jeff, then the common language runtime prevents the unsafe cast and raises the InvalidCastException exception. in the first line, I create an object type is System. Object, in the second line, I tried to convert the reference to the system.object type into a JEFF type. Both lines of code can be compiled correctly. However, at runtime, the second line of code produces an InvalidCastException, and if this exception is not captured, the program will terminate.
When the second line of code is run, the generic language runtime verification object O reference is a JEFF type, if so, the general language runtime allows for conversion. If the objects and Jeff, the Otte of the O reference does not matter or the base class of Jeff, the universal language runtime will disable this insecure conversion and produce an exception.
C # offers another way to perform a cast using the as operator:
Jeff J = New Jeff (); // Create a new Jeff Object Creates a new JEFF object
System.Object O = J as system.object; // Convert to Object
// o Now Refers to the Jeff Object O Now reference a Jeff object
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 can not 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 concepter.
The AS operator is used to try to convert the object into a specified type. However, different from ordinary conversion, the AS operator does not throw an exception. Moreover, if the type of object cannot be successful, NULL will be returned. When using the wrong reference, NullReferenceException will throw this exception. The following code demonstrates this concept.
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 NullReferenceExceptionIn 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.
In order to match the AS operator, C # also provides an IS operator. The IS operator checks if an object is a type, and the return value is TRUE or FALSE. The IS operator does not produce an exception.
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 The the No Object Available To Check ITS Type.
To make one you understand everything just presented, Assume That The Following Two Class Definitions exist.
Note that if the object reference is NULL, the IS operator returns false because there is no object to check the type.
Class b {
INT X;
}
Class D: b {
INT X;
}
NOW, Check Figure 2 To See Which Lines Would Compile and Execute Success, (ES), Which Would Cause A Compiler ERROR (CE), and Which Would Cause A Common Language Runtime Error (RE).
Please see Figure 2, which line will compile the operation correctly, which line will generate compilation errors, which line will generate an error.
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.
Collection and Namespaces
Types of the same class can be combined together to form a collection. In the collection, there is a separate Namespace. For developers, Namespace is like some logical groups. For example, the base class library collection includes many Namespace. System Namespace core low-level types such as Object base classes, Byte, INT32, Exception, Math, And Delegate, and System.Collections Namespace include these types of 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 name.
For compilers, Namespace is a simple way to ensure the name of the type, different names and symbols are separated. An Object class can write System.Object in System Namespace. Similarly, the Queue class can write SYSTEM.COLLECTIONS.QUEUE in System.Collections Namespace.
The runtime engine does not 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 askEMBLY, FIND The TYPE, AND MANIPULATE IT.
Running Engine doesn't know that there is NameSpace, when you visit a type, the general language runtime needs to know the full name of the type and which collection has this type of definition so that the universal language runtime can properly load the lookup and operation.
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 Source Code Modules:
Programmers often want to express the formula with the easiest way, which is more difficult to use a full name reference class. Therefore, many program languages provide an instruction that allows the compiler to add a prefix. When using C #, I usually add the following statement at the beginning of the program. 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 not 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.
When I quote a type in the code, the compiler needs to get this type of definition and guarantee that my code can access this type correctly. If the compiler cannot find the type of definition, it will try to add "System" prefix to find. The previous line code allows me to access the Object class, the compiler automatically turns the name into System.Object. It is conceivable that it will make a lot of words less.
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.
When the type is defined, the compiler needs to know which collection contains the type definition information to add it to the result file. In order to obtain collection information, you have to give a collection of reference 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 FooBar & # 0;. Microsoft'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. You can do some potential problems. For the convenience of programming, you should try to avoid using the name that may cause conflicts. However, in some cases, it is simply impossible. .NET advocates component reuse. Your program may use Microsoft components and another component. Both components are called Footbar, but the functions of the two components are completely different. In this case, you cannot control the object only by type name, quote Microsoft's Footbar, you have to use Microsoft.footbar, quote my footbar to use 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 will not discover a problem until runtime:
In the following code, the reference to FootBar is unclear. The compiler is best tolerant here, but the C # compiler will choose an available type and know if you are running.
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.
In order to remove the unclear reference, you must clearly tell the compiler that you want to create which 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 do not want to pollute the global namespace with all of a Namespace's Types. The Following Code Demonstrates Another Way To Solve The Ambiguity Problem. Another way is to create an alias for the simple type. If you only quote a few types from a Namespace, don't want to affect all Namespace types, which is relatively simple. The following code demonstrates how to solve the problem that is not clearly referenced.
// 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 scenarios where 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 assembly, NOT JUST BETWEEN NAMESPACES.
This method of eliminating ambiguity is useful, but it is not good enough. If Australian Boomerang Company (ABC) and the alaskan boat corporation (ABC) creates a type called BuyProduct, and is intended in their respective collections. Both companies will create ABC Namespace containing BuyProduct types. If a developer needs to purchase Boomrangs and Boats, you will have trouble unless your programming language provides a program to distinguish two collections.
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 (not an acronym) to be your top-level namespace name name to reduce The likelihood of conflict. You can see That Microsoft Uses a namespace of "Microsoft". Unfortunately, the C # instruction only supports NameSpace without providing a way to distinguish a collection. However, this problem in the real world does not happen. But it is indeed a problem. If your designed components want to use third-party, it is recommended that you use NameSpace to define types so that the compiler can distinguish between types. In fact, you should use the company's full name to reduce the possibility of conflicts. Microsoft uses "Microsoft"
Creating a namespace is Simply a Matter of Writing a namespace Declaration Into your code, as Follows:
Create NameSPCE and simple, write the Namespace declaration as follows.
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 can not change this by including any access modifiers. However, you can define types within a namespace that are internal (can not be used outside the assembly) or public (can be accessed by any assembly). ..............................
Note that Namespace is public. You cannot rely on any access to modification. You can define internal and external types in NameSpace, namespace represents logical contained.
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 at A column, I will tell different types of .NET developers must understand the original type, reference type, and value type. A thorough understanding of the value is especially important to each .NET developer