C ++: support the most powerful language of the .NET program design

xiaoxiao2021-03-06  110

C : support the most powerful language of the .NET program design

C : The Most Powerful Language for .NET Framework Programming PROGRAMMING

C : support the most powerful language of the .NET program design

Introduction

The VC team spent a lot of time to listen to users who use .NET and C , decided to redesign the support capabilities of VC in CLR. The new design is called C / CLI, which is planned to use and create managed types with more natural syntax. Here are new grammar and comparison with C #.

Common Language Infrastructure (CLI) is a set of specifications and is an infrastructure that makes up .NET. CLR is an implementation of the CLI. The design of C / CLI provides natural and simple CLI support, while the VC 2005 compiler makes C / CLI compatible CLR.

Two messages are obtained when you understand the upcoming VC2005 compiler and C / CLI. First, the VC intended to be positioned as the bottom-up language for CLR development, and there will be no reason to choose other .NET language, including IL assembly. Second, it will be programmed in a way that is close to native C programming. These two messages will become more and clear in the process of reading this article.

This article is written to C programmers, I don't plan to persuade you from C # or VB.NET here. If you like C and hope to get all traditional C language powerful advantages, it is necessary to efficient productivity as C #, then this article is prepared for you. In addition, this article does not provide a introduction of CLR or .NET Framework, but pays attention to how the VC2005 allows you to write elegant and efficient .NET-oriented code.

Object Construction

CLR defines two types-reference types and value types. The value type is designed for high-speed creation and access, and their performance is very similar to the C built-in type, and you can also create your own value type. That's why Bjarne Stroustrup calls the curing type. The reference type is to provide all object-oriented needs and characteristic design, such as grading capabilities, just like these: inherited objects, virtual functions, and so on. With CLR, the reference type also provides other runtime features, such as automatic memory management (is a widely known GC). The CLR also provides a deeper type and reference type to provide a deeper level to get the ability to get type information at runtime, which is derived from the reflection.

The value type is assigned to the stack, and the reference type is on the hosted stack, which is a heap of the CLR's garbage collector (GC) management. If you use C to write an assembly, you can put the types written in these native C on the CRT pile as before. After later, the VC team will try to let you write the type written by the native C on the hosted stack. After all, the GC is equally attractive for native code.

The native C language allows you to decide to create a new object. Any type can be selected to be placed on the stack or a CRT heap.

// Plan on stack std :: wstring stackobject; // put on the CRT Pile on std :: wstring * HeapObject = new std :: wstring;

As you can see, where the object is placed and the object type is not related to the type of object, but a complete procedure control. Note that the syntax of the object is allocated on the pile, and the nothing on the stack is not.

In another aspect, C # allows the object to create value types on the stack, and create a reference type object on the stack. The System.DateTime class (in several examples will be used later) is defined by his author as a value type.

// put on the stack system.datetime stackObject = new system.datetime (2003, 1, 18); // Plan on System.io.MemoryStream HeapObject = New System.io.MemoryStream (); like you It's not, you don't have any way to control the object of the defined type is created on the stack or in the stack, this choice is completely dependent on the type of designer and running environment (Runtime).

C hosted expansion, introduced simple mixing for native C code and hosted code creation. Extending C to support C to support a wide range of CLR components. However, unfortunately, there is too much extension here, so that the hosting code written with C has become a huge pain.

// Plan on the stack with DateTime StackObject (2003, 1, 18); // Play in the hosted pile IO :: MemoryStream __gc * HeapObject = __GC new IO :: MemoryStream;

Assign the value type to the code on the stack and the original C code is very similar. However, the code of the hosted stack, it looks a little strange. __gc is a keyword for hosting C . As already proven, hosting C can also inform you in some cases, so this example can also be rewritten, without the __gc keyword, this is a well-known default behavior.

// Put in the hosted pile IO :: MemoryStream * HeapObject = new IO :: MemoryStream;

This is more like native C code. But the problem is that the HeapObject is not a real C pointer. C programmers will always trust the existence of the object they created, but the GC may remove these objects at any time. Another obstacle is that there is no way to explicitly control the object to allocate memory on the native or hosted pile. You need to know how the type is defined by its authors. In addition, there is a large amount of strong evidence to prove that the significance of redefining the C pointer is a bad note.

C / CLI brings a new handle concept to distinguish between CLR object references and C pointers. It cancels the manner of overloading C pointers, so that a large amount of content is removed from this language. In addition, more natural CLR support can be provided through the handle. For example, you can use the operator overloaded on the reference type in C because it is supported in the handle. This may not be implemented in the "hosting" pointer, as C is prohibited from being operated on the pointer.

// Plan on the stack with DateTime StackObject (2003, 1, 18);

// Put in the hosted pile IO :: MemoryStream ^ HeapObject = GCNEW IO :: MemoryStream

Here we also do not define the doubts when the value type object is defined, but the reference type is not the same. Operator ^ defines this variable as a handle of a CLR reference object. The trajectory of the handle means that the content of the handle is that the object he pointed to be automatically updated by the GC, which will continue to move in memory. In addition, they can support resin, which allows them to point to a plurality of different objects, just like a normal C pointer. Another thing you must pay is that the keyword GCNEW takes up the original NEW position, which clearly marks this object will be assigned to the hosted stack. The NEW keyword is no longer overloaded for managed types (no double meaning), but is just assigning objects on the CRT pile - of course unless you provide your own New operator. What reasons do you do not like C !

The creation of this object has been solid support: the native C pointer and CLR object reference are clearly distinguished. Memory Management vs. Resource Management, MANAGEMENT

It is very useful when you face an environment with a garbage collector (GC). It is very useful to separate memory management and resource management. Typically, GC is used to release and assign the memory required by your object, which does not care about the resources occupied by other objects, such as a database connection or a handle of the kernel object. In the following sections, I will discuss memory management and resource management, as they are critical to understanding this article.

Memory Management

Native C allows programmers to have direct operating memory. Place the object on the stack means that the object will be created when entering a specific function, and the memory used will be released when the function returns the stack. Dynamic build objects are completed using the New keyword, and the memory used in the CRT heap, and the memory release must explicitly complete through the DELETE operator using the pointer. This kind of control over memory makes C can be used as the development of high performance programs, but if the programmer is not careful, it is easy to cause memory disclosure. Obviously, you don't have to rely on GC to avoid memory leaks, but in fact, the CLR uses this method and is a very efficient approach. Of course, the GC hosted stacks have other benefits, such as increasing the performance of allocating memory and content related to memory addressing. Although all of these uses can be done in C through library, this mechanism established on the CLR is conducive to establishing a unified memory management programming model, which is the same for all languages. Think about how to operate COM components, you may have this idea. If there is a universal garbage collector, this will greatly reduce programmed workload.

Obviously CLR retains the concept of stack in order to improve the operating value type. However, the CLR also provides a NewobJ IL instruction for establishing an object on the hosted stack. This instruction is used when using a New operator in C #. There is no feature of any and delete operator in the CLR. The previously allocated memory is always recycled. When the program no longer has reference to an object, the GC will automatically perform memory collection operations.

The hosted C also generates a Newobj IL instruction for the New Otte acting on the reference type, however it is not legal when using the Delete operator to operate the host / pointer. This is obviously a pair of annoying contradictions. This is another reason for proved the intensity of heavy-duty C pointer.

C / CLI does not bring any new content for memory management, just like we have hinted in the last section. However, resource management is a place in C / CLI.

Resource Management

So far, there is no language in resource management that can exceed C . Bjarne Stroustrup's "Resource Acquisition IS Initialization" technology fundamentally defines all resources to provide constructors and destructors (for release resources) as class models. These types can be used for variables on the stack, or as a more complex type. Their destructure can automatically release the resources they hold. Stroustrup said, "C is The Best Language for Garbage Collection Principally Because It Creates LESS Garbage."

Some strangely, for the resource management CLR does not provide any direct runtime support, the CLR does not support the destructor in the C sense. Also, .NET Framework has used resource management abstraction to a core interface called IDisposable, intended to all encapsulated a class of resources to implement the unique Dispose method of this interface, and call Dispose when the caller no longer needs these resources. method. Don't say more, C programmers often think this way is to retreat, because they have already been accustomed to the default for resource cleaning operations. The trouble must be made by calling a function to make the resource cleanup is more difficult to write an exceptionally secure code. You can't simply put the call of the Dispose method in the final block, the exception may pop up at any time, so it is possible to lead to the leakage of resources. C # Solve this problem by providing Try-Finally and Using declarations, which provides a trusted way to call the Dispose method without worrying about the problem caused. But these structures sometimes bring trouble, even worse, you must always write these code, if you forget, then the compiler will still compile the hidden problem. So very unfortunately, for the lack of true destructor, try-finally or using declaration is indispensable.

Using (SqlConnection Connection = New SqlConnection ("Database = Master; Integrated Security = SSPI")))))

{

Sqlcommand command = connection.createCommand ();

Command.comMandText = "sp_databases";

Command.commandtype = commandtype.storedProcedure;

Connection.open ();

Using (SqlDataReader Reader = Command.executeReader ())

{

While (Reader.Read ())

{

Console.writeLine (Reader.getstring (0));

}

}

}

There is also the same problem in hosted C . You have to use the try-finally declaration, which is also an extension of Microsoft as C . Host C does not have a similar equivalent structure with USING in C #, but we can easily write a USING template class wrapped in Gchandle, which can call the object's Dispose method .

Using Connection (New SqlConnection)

(S "Database = Master; Integrated Security = SSPI"))))

SQLCommand * command = connection-> createcommand ();

Command-> set_commandtext (s "sp_databases");

Command-> set_commandtype (commandtype :: storedprocedure);

CONNECTION-> open ();

Using Reader (Command-> ExecuteReader ());

While (reader-> read ())

{

Console :: WriteLine (Reader-> getString (0));

}

Imagine a traditional C 's strong support for resource management, C / CLI is already like C to turn resource management into a gentle thing. Let's take a look at a class that manages a resource. It generally implements the CLR's Dispose mode, and cannot establish a sectator like the native C . When you write the Dispose function, you also need to guarantee the DISPOSE function of the parent class (if any). In addition, if you choose to use Finalize to call the Dispose method, you will have a concurrent conflict, because the Finalize method is running on another independent thread; and you need to guarantee that you may run in the normal program code. At the same time, be careful to release resources in the finalize method. C / CLI did not solve all problems, but at least a great help. Before viewing it, let's look back now in the current C # and managed C processing. This example racks base class base implements the idisposable interface (if not, this derived class may not need).

Class Derived: Base

{

Public Override Void Dispose ()

{

Try

{

// Free management and unmanaged resources

}

Finally

{

Base.dispose ();

}

}

~ Derived () // Implements / Overrides The Object.Finalize Method

{

// free unmanaged resources ONLY

}

}

Host C is also very similar, that looks like a destructor is actually a Finalize method. The compiler will be careful to add a Try-FinalLy block to ensure that the Finalize method of the base class is called, so C # and managed C provide simple and effective ways to implement the Finalize method, but does not provide any help for the implementation of the Dispose method - more important thing. Programmers can only use Dispose as a destructor, in fact, this is only a descent, not forced.

C / CLI puts Dispose as a logically "sectator" in the reference type, further emphasizes its importance.

Ref class deive: base

{

~ Derived () // Implements / Overrides The IDisposable :: Dispose Method

{

// Free management and unmanaged resources

}

! Derived () // Implements / Overrides The Object :: Finalize Method

{

// free unmanaged resources ONLY

}

}

This looks more like C programmers familiar with, we can release resources in the destructor as before. The compiler will automatically add the necessary IL code to implement the IDisposable :: Dispose method, including the Finalize method above the GC call object. In fact, implementing the Dispose method in C / CLI is illegal, inheriting IDisposable will result in a compiler prompt error. Of course, once a type compiles, all CLI languages ​​that use this type will think it implements Dispose mode. In C # can call the Dispose method directly, or use the USING statement to do, just like this is class written with C #; but C ? How do you call the destructor of the object on the heap? Of course use the delete operator! Use the DELETE operator on the handle to call the Dispose method of the object. Recalling the memory block occupied by GC management, we don't guarantee the release of this memory, but can guarantee the resource occupied by the object immediately. Derived ^ D = gcnew deerived ();

D-> SomeMethod ()

Delete D;

So if the expression to the Delete operator is a handle, the object's Dispose method is called. If no path is connected to this object, GC will clean up the memory occupied by this object when it is appropriate; if it is a native C object, the destructor will be called before returning to the heap.

Obviously, in the lifecycle management of the object, we and the original C syntax is approaching step, but there is still a mistake tendency to use the delete operator. The C / CLI allows the use of stack semantics on the reference object, which means we can use some existing syntax to "put" in the stack. The compiler will handle this kind of semantics you might as you want, in order to meet the needs, actually objects are placed on the hosted stack.

Derived D;

D.somethod ();

When the D leaves the effective scope, its DISPOSE method is called to release the resource. As mentioned above, because the object is actually created on the hosted pile, the GC will release the memory itself in a certain time itself. Back to our ADO.NET, now use C / CLI to be written as this:

SqlConnection Connection ("Database = Master; Integrated Security = SSPI");

Sqlcommand ^ command = connection.createCommand ();

Command-> CommandText = "sp_databases";

Command-> CommandType = commandtype :: storedprocedure;

Connection.open ();

SqlDataReader Reader (Command-> EXECUTEREADER ());

While (Reader.Read ())

{

Console :: WriteLine (reader.getstring (0));

}

Types Revisited

Cleaning the value type and reference types will be helpful before discussing the box / unpacking.

You can imagine the value type is a simple value, and an instance of the reference type is an object. You don't consider that memory needs to store each field of the object, all objects in the object-oriented program have a header information, such as virtual functions, and other metadata can be used as a variety of purposes. However, in object header information - these virtual methods and interfaces repeated lookup operations will often be very large - and you need to just a simple object, and just need to operate in the compilation phase. It can be proven that the compiler can optimize the operation on this object in some cases, but not all. If you care about this performance problem, it is clear that the value and value type are very beneficial. This is not to be separated from the type of C . Of course, C does not impose any programming paradigm, so it is feasible to establish such a different type of system on C instead of the creation of the library. Boxing

What is a packing? Packing is a bridge between fill values ​​and objects between objects. Although the CLR requires all types to inherit the object type directly or indirect, the actual value type object is not. Simple types like integers on the stack are just a memory, and the compiler guarantees that it can operate directly. If you really need to treat a value as an object, then it should be an object so you should be able to call the Object from your value on your value. In order to achieve this requirement, the CLR provides the concept of packing. It's a bit safe to understand the process of this packing. First, a value is pressed on the stack by the IL command ldloc, and then a set of boxed IL instructions start running: The compiler provides static type information, such as INT32, then the CLR makes this value out of the stack and distributes a sufficient space in the hosted heap. This value and object header information, a reference to this newly created object is pressed into the stack. All of this constitutes a packing instruction. Finally, to get the reference to this object, the IL instruction STLOC is used to pop up this reference from the stack and stored on a particular local variable.

Then now the problem is for programming languages, packing operations should be implicit or explicitly proceed. In other words, do you need an explicit type conversion, or use other configurations? C # language designer decided to use implicit conversions, after all, the integer is indeed indirectly inherited to the Object's INT32 type.

INT i = 123; Object O = i;

However, as we have seen, the packing operation is not a simple-top type conversion, which is a power-to-object conversion, a potential expensive operation. It is for this reason that the hosted C uses the __box keyword to make the boxed operation must be explicit.

INT i = 123; Object * o = __box (i);

Of course, in the hosted C , you don't need to lose static type information when you pack the box, which is not available in C #.

INT i = 123; INT __GC * o = __box (i);

Strong type packing operations bring convenience when the conversion value is, that is, well-known unpacking, we don't need to use dynamic conversion syntax, just cancel the object directly.

INT C = * O;

Of course, the syntax of explicit packing in the hosted C has proven to be some redundant. For this reason, the design policy of C / CLI has changed, and it is implicitly packaged with C #. At the same time, we can still perform strong types of packing operations in a type of safe way, this is what other .NET languages ​​cannot be done.

INT i = 123; int ^ hi = i; int C = * hi; hi = NULLPTR;

Of course, this implies indicates that the handle that does not point to any object should not be initialized to zero, although it can point to the value of the boxed 0, but the pointer is not. This is the original origin of the design constant NullPtr. It can assign a value to any handle. It is equivalent to the Null keyword of C #. Although NullPtr is just a new reserved word in the C / CLI design, it is now available to the C standard by Herb Sutter and Bjarne Stroustrup. Authoring Reference and Value Types

In the following sections, we will review some of the details of the CLR type. // ..

AccessibilityPropertiesDelegateSconClusion

There are still a lot of C / CLI, and there is no need to worry about the VC2005 compiler itself, but I hope this article can make you understand what it can bring for programmers. The newly designed language provides an unprecedented ability and elegant encoding method for .NET programs, without any sacrifice in productivity, simplicity and performance.

In the table below, there is a common syntax structure comparison

Description C / CLIC # Create a reference type of object Referenceype ^ h = gcnew reference type; Referenceype H = New ReferenceYpe (); Create value type object valueetype V (3, 4); valueType v = new valuePE (3, 4); reference Type in the stack Referenceype H; N / A call dispose method Referenceype ^ h = gcnew reference ^ h = GCNew Referenceype; Deference H; Referenceype H = new reference (); (iDisposable) h) .dispose (); implement Dispose method ~ typeName () { } void idisposable.dispose () {} implements a Finalize method! Typename () {} ~ typeName () {} box (boxing) int ^ h = 123; Object H = 123; unboxing int ^ Hi = 123 ; int c = * hi; object h = 123; int i = (int) h; defining reference type Ref class referencetype {}; ref struct refresses}; class referenceType {} definition value type value class valueetype {}; value struct ValueType {}; struct valuepe {} uses attribute h.prop = 123; int v = H.Prop; h.prop = 123; int v = h.prop; define Properties Property String ^ name {string ^ get () {Return M_Value;} void set (string ^ value) {m_value = value;}} String name {get {return m_name;} set {m_name = value;}}

About the Author:

Kenny Kerr spends most of his time designing and building distributed applications for the Microsoft Windows platform. He also has a particular passion for C and security programming. Reach Kenny at http://weblogs.asp.net/kennykerr/ or visit his Web site About the translator: http://www.kenny/. About the translator:

Sunmast@vip.163.com

Posted on Thursday, August 19, 2004 9:20 PM

转载请注明原文地址:https://www.9cbs.com/read-103592.html

New Post(0)