Top Ten traps in C # for C + + programmers Chinese version (turn)

Source: Internet
Author: User
Tags garbage collection inheritance reference resource thread visual studio
c++| Chinese "preface: C # Introductory article. Please note that all program debugging environments are Microsoft Visual Studio.NET 7.0 Beta2 and Microsoft. NET Framework SDK Beta2. Limited to the translator's time and ability, if there are corruption in the text, the original English will prevail.

In an article recently published in the MSDN Magazine (July 2001 issue), I said, "What should you know about moving from C + + to C #?" ”。 In that article, I said that C # and C + + syntax are very similar, the difficulty in the transfer process is not from the language itself, but to the regulated. NET environment and understanding of the large. NET Framework.

I've edited a list of different points in C + + and C # syntax (this list can be found on my web site). At the site, click Books to browse "Programming C #", or click on the FAQ to see. As you would expect, many grammatical changes are small and trivial. Some of the changes are even hidden traps for unwary C + + programmers, and this article will focus on 10 dangerous pitfalls.

Trap one. Non-deterministic finalization and C # destructor

Of course, for most C + + programmers, the biggest difference in C # is garbage collection. This means you no longer have to worry about memory leaks and the problem of ensuring that pointer objects are deleted. Of course, you also lose the ability to accurately control the timing of destroying objects. In fact, C # does not have an explicit destructor.

If you're dealing with an unregulated resource, you need to explicitly release those resources when you're done. You can implicitly control a resource by providing a Finalize method, called a finalizer, that is invoked by the garbage collector when the object is destroyed.

Finalizers should only release uncontrolled resources carried by objects and should not refer to other objects. Note: If you have only a few controlled object references then you don't have to and should not implement the Finalize method-it is only used when you need to handle uncontrolled resources. Because there is a cost to using finalizers, you should only implement them in the way you need them (that is, by using costly, unregulated resources).

Never call the Finalize method directly (except in the Finalize method of calling the base class in the Finalize of your own class). There seems to be a mistake here, see below! "), the garbage collector will help you call it.

C # 's destructor resembles the C + + destructor in syntax, but they are different in nature. The C # destructor is just a shortcut to declaring a Finalize method and chaining it to its base class. This means that when an object is destroyed, from the lowest to the top of the most derived level, the destructor is invoked in turn, see the complete example given later. Therefore, the following wording:

~myclass ()

{

Do work here

}

Has the same effect as the following:

Myclass.finalize ()

{

Do work here

Base. Finalize ();//

}

The above code is clearly wrong and should first be written as:

Class MyClass

{

void Finalize ()

{

Do work here

Base. Finalize ()//This is not possible! The compiler will tell you that you cannot directly call the base class's Finalize method, which will be invoked automatically from the destructor. For the reasons, see the example later in this section and trap two.

}

}

A complete example is given below:

Using System;

Class Rytestparcls

{

~rytestparcls ()

{

Console.WriteLine ("Rytestparcls ' s destructor");

}

}

Class Rytestchldcls:rytestparcls

{

~rytestchldcls ()

{

Console.WriteLine ("Rytestchldcls ' s destructor");

}

}

public class Rytestdstrcapp

{

public static void Main ()

{

Rytestchldcls RTCC = new Rytestchldcls ();

RTCC = null;

Gc. Collect ()//Mandatory garbage collection

Gc. WaitForPendingFinalizers ()//suspends the current thread until the thread that processes the finalizer queue empties the queue

Console.WriteLine ("GC completed!");

}

}

The above program output results are:

Rytestchldcls ' s destructor

Rytestparcls ' s destructor

GC completed!

Note: In the CLR, you implement a virtual method by overloading the System.Object virtual method, Finalize (), in C #, it is not allowed to overload the method or call it directly, and the following is an error:

Class Rytestfinalclass

{

Override protected void Finalize () {}//Error! System.Object methods cannot be overloaded.

}

Similarly, the following wording is also wrong:

Class Rytestfinalclass

{

public void Selffinalize ()//Note! The name was taken by himself, not finalize.

{

This. Finalize ()//Wrong! Cannot call Finalize () directly

Base. Finalize ()//Wrong! Cannot directly call base class Finalize ()

}

}

Class Rytestfinalclass

{

protected void Finalize ()//Note! The name is not the same as the above, and it is not override, it is possible, so that you hide the base class of finalize.

{

This. Finalize ()//adjust yourself, of course, but this is a recursive call you want? J

Base. Finalize ()//Wrong! Cannot directly call base class Finalize ()

}

}

For a complete understanding of the subject, please refer to trap two. 】

Trap two. Finalize and Dispose

It is illegal to call Finalizers explicitly, and the Finalize method should be called by the garbage collector. If you are dealing with a limited, unregulated resource (such as a file handle), you may want to close and release it as soon as possible, and you should implement the IDisposable interface. This interface has a Dispose method, which performs a purge action. The client of the class is responsible for explicitly calling the Dispose method. The Dispose method allows the customer of the class to say, "Don't wait for finalize, do it now!" ”。

If you provide a Dispose method, you should prevent the garbage collector from calling the object's Finalize method-now that you want to explicitly clear it. To do this, you should call the static method Gc.suppressfinalize and pass in the object's this pointer, and your finalize method will be able to call the Dispose method.

You might write this:

public void Dispose ()

{

Perform a purge action

Tell the garbage collector not to call finalize

Gc. SuppressFinalize (this);

}

public override void Finalize ()

{

Dispose ();

Base. Finalize ();

}

"The above code is problematic, please refer to the example I gave in the trap one." There is a very good article on the Microsoft site (Gozer the destructor), which is basically consistent with this, but its code example is in Microsoft Visual Studio.NET 7.0 Beta2 and Microsoft. NET Framework SDK Beta2, because there is no Beta1 on hand, so, now is not sure is the article clerical error, or because of Beta1 and Beta2 differences, or I did not accurately understand the problem. For example, the following example (from Gozer the destructor) cannot be passed in the BETA2 environment:

Class X

{

public X (int n)

{

THIS.N = n;

}

~x ()

{

System.Console.WriteLine ("~x () {0}", n);

}

public void Dispose ()

{

Finalize ();//This line of code has an error in the BETA2 environment! compiler hint, finalize cannot be invoked, consider calling IDisposable.Dispose (if available)

System.GC.SuppressFinalize (this);

}

private int n;

};

Class Main

{

static void F ()

{

X x1 = new X (1);

X x2 = new X (2);

X1. Dispose ();

}

static void Main ()

{

f ();

System.GC.Collect ();

System.GC.WaitForPendingFinalizers ();

}

};

And the article claims to have the following output:

~x () 1

~x () 2

Why 】

For some objects, you might prefer to have your client call the Close method (for example, close is better than Dispose for a file object). You can then call Dispose in close by creating a private Dispose method and a public close method.

Because you are not sure that the client will call Dispose and the finalizer is indeterminate (you cannot control when the GC is run), C # provides a using statement to ensure that Dispose is called as early as possible. This statement is used to declare what objects you are using, and to create a scope for these objects with curly braces. When the "}" J is reached, the Dispose method of the object is automatically invoked:

Using System.Drawing;

Class Tester

{

public static void Main ()

{

using (Font Thefont = new Font ("Arial", 10.0f)

{

Using Thefont

}//Compiler calls Dispose for Thefont

Font anotherfont = new Font ("Courier", 12.0f);

using (Anotherfont)

{

Using Anotherfont

}//Compiler calls Dispose for Anotherfont

}

}

In the first part of the example, the Thefont object is created within the using statement. When the scope of the using statement ends, the Dispose method of the Thefont object is invoked. In the second part of the example, a Anotherfont object is created outside the using statement, and when you decide to use the Anotherfont object, you can put it inside the using statement, and the Dispose method of the object is also invoked when you reach the end of the using statement's scope.

The using statement also protects you from handling unexpected exceptions, regardless of how the control leaves the using statement, and the dispose is invoked as if there is an implicit try-catch-finally program block.

Trap three. C # Differentiating value types and reference types

Like C + +, C # is a strongly typed language. And like C + +, C # divides types into two classes: the intrinsic (built-in) type provided by the language and the user-defined type of the programmer's definition, called the UDT.

In addition to distinguishing between intrinsic and user custom types, C # also distinguishes between value types and reference types. As with variables in C + +, value types store values on the stack (except for the value types embedded in the object). The reference type variable itself is on the stack, but the object they point to is on the heap, much like the pointer in C + +, which is actually more like the reference J in C + +. When passed to a method, the value type is a pass value (a copy made) and a reference type is passed efficiently by reference.

Classes and interfaces Create reference types "This is a bit vague, it is not possible to directly create an object of an interface type, and not every kind of type is acceptable, but you can assign references to instances of their derived classes (when it comes to" class type ", you can't help but think about the" type "word" ups and downs ")", But keep in mind (see Trap V): As with all intrinsic types, structs are also value types.

"I can see the example of trap five."

Traps four. Beware of implicit boxing

Boxing and unboxing are procedures that enable value types, such as integers, to be processed as reference types. The value is boxed into an object, and the subsequent unboxing restores it to a value type. Each type in C # includes an intrinsic type that derives from object and can be implicitly converted to object. Boxing a value is equivalent to creating an object and copying that value into that object.

Boxing is implicit, so when a reference type is required and you provide a value type, the value is implicitly boxed. Boxing brings some execution burden, so avoid boxing as much as possible, especially in a large set.

If you want to convert a boxed object back to a value type, you must explicitly remove it. The unboxing action is divided into two steps: first examine the object instance to ensure that it is a boxed object of the value type that will be converted, and if so, copy the value from the instance to the target value type variable. In case of a successful unboxing, the object being removed must be a boxed object reference to the target value type.

Using System;

public class Unboxingtest

{

public static void Main ()

{

int i = 123;

Packing

Object o = i;

Unboxing (must be done explicitly)

int j = (int) o;

Console.WriteLine ("J: {0}", j);

}

}

If the object being removed is null or a boxed object reference that is different from the target type, then a InvalidCastException exception is thrown. "It is wrong to say that if the object being removed is null, it throws a System.NullReferenceException instead of a System.invalidcastexcepiton"

"On this subject, I have a more wonderful description in another translation (a comparative Overview of C # in Chinese (above))," said J "

Trap Five. The structure in C # is very different

The structure in C + + is almost the same as the class. In C + +, the only difference is that the struct "means member" by default has public access (rather than private) level and the inheritance defaults are public (again, not private). Some C + + programmers think of structs as objects that have only data members, but this is not an agreement that the language itself supports, and is not encouraged by many OO designers.

In C #, structs are a simple user-defined type, a lightweight alternative that is very different from the class. Although structs support properties, methods, fields, and operators, structs do not support things like inheritance or a destructor.

More importantly, the class is a reference type, and the struct is a value type (see Trap three). Therefore, structs are useful for objects that do not need to refer to semantics for performance. Using structs in arrays is more efficient in memory, but if you're in a collection, it's not that efficient--the collection needs a reference type, so if you use the struct in the collection, it has to be boxed (see Trap Four), and boxing and unboxing require an extra load, so the class might be more efficient in a large set.

"The following is a complete example, it also demonstrates the implicit type conversion, please observe the program and its operating results J

Using System;

Class Rytestcls

{

public rytestcls (int aint)

{

This. Intfield = aint;

}

public static implicit operator RYTESTCLS (RYTESTSTT RTS)

{

Return to New Rytestcls (RTS. Intfield);

}

private int Intfield;

public int Intproperty

{

Get

{

return this. Intfield;

}

Set

{

This. Intfield = value;

}

}

}

struct RYTESTSTT

{

public ryteststt (int aint)

{

This. Intfield = aint;

}

public int Intfield;

}

Class Ryclsstttestapp

{

public static void Processcls (Rytestcls RTC)

{

Rtc. Intproperty = 100;

}

public static void Processstt (RYTESTSTT RTS)

{

Rts. Intfield = 100;

}

public static void Main ()

{

Rytestcls RTC = new Rytestcls (0);

Rtc. Intproperty = 200;

PROCESSCLS (RTC);

Console.WriteLine ("RTC.") Intproperty = {0} ", RTC. Intproperty);

RYTESTSTT RTS = new RYTESTSTT (0);

Rts. Intfield = 200;

PROCESSSTT (RTS);

Console.WriteLine ("RTS"). Intfield = {0} ", RTS. Intfield);

Ryteststt rts2= New Ryteststt (0);

Rts2. Intfield = 200;

Processcls (RTS2);

Console.WriteLine ("Rts2.") Intfield = {0} ", Rts2. Intfield);

}

}

The results of the above programs are:

Rtc. Intproperty = 100

Rtc. Intfield = 200

Rts2. Intfield = 200





Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.