50 suggestions for improving C # programming (16-20)

Source: Internet
Author: User

16 avoid creating unnecessary objects
GC (garbage collection) manages memory for us and removes unused objects in a more effective way. However, no matter how the heap-based objects are allocated or destroyed
It will take a lot of processing time, so please do not increase the GC burden, the following is a bad way to allocate GDI objects:

    protected override void OnPaint(PaintEventArgs e)    {        // Bad. Created the same font every paint event.        using (Font MyFont = new Font("Arial", 10.0f))        {            e.Graphics.DrawString(DateTime.Now.ToString(),            MyFont, Brushes.Black, new PointF(0, 0));        }        base.OnPaint(e);    }

The OnPaint event is frequently called, and each call to this event creates a Font object. GC needs to clean these objects every time, which is very inefficient.
It would be better if we could change the Font to a member variable at this time:
    private readonly Font myFont =new Font("Arial", 10.0f);    protected override void OnPaint(PaintEventArgs e)    {        e.Graphics.DrawString(DateTime.Now.ToString(),        myFont, Brushes.Black, new PointF(0, 0));        base.OnPaint(e);    }

For a string object, we should also note that the content of the string object remains unchanged. Once you create a string object, its content cannot be modified. When you seem to be modifying its content, actually, a new object is created, and the old object is used
Garbage collection:
String msg = "Hello ,";
Msg + = thisUser. Name;
Msg + = ". Today is ";
Msg + = System. DateTime. Now. ToString ();
This Code corresponds:
String msg = "Hello ,";
// Not legal, for authentication only:
String tmp1 = new String (msg + thisUser. Name );
Msg = tmp1; // "Hello" is garbage.
String tmp2 = new String (msg + ". Today is ");
Msg = tmp2; // "Hello "Is garbage.
String tmp3 = new String (msg + DateTime. Now. ToString ());
Msg = tmp3; // "Hello . Today is "is garbage.


Tmp1, tmp2, tmp3 and the original msg object are all garbage collected by GC. The + = Operator on string actually creates a new string object and returns it.
In this case, you should use the string. Format () method to avoid creating new objects:
String msg = string. Format ("Hello, {0}. Today is {1}", thisUser. Name, DateTime. Now. ToString ());
For more complex string operations, you can use the StringBuilder type:
StringBuilder msg = new StringBuilder ("Hello ,");
Msg. Append (thisUser. Name );
Msg. Append (". Today is ");
Msg. Append (DateTime. Now. ToString ());
String finalMsg = msg. ToString ();
Although GC effectively manages the memory, creating redundant objects will consume the processor time. Therefore, avoid creating too many objects and avoid creating unnecessary objects.


17. Implement the standard Dispose Mode
A standard mode is to process unmanaged resources through the. NET Framework. Users who use your class also expect you to follow this standard mode. The standard mode is to use the IDisposable interface to release your unmanaged resources. When the client forgets to do so, it will use finalizer for passive release. It works with GC to ensure that your object is terminated when necessary. This is the correct way to handle unmanaged resources, so you should fully understand it.
The underlying class at the class level should implement the IDisposable interface to release resources. This class should also add a finalizer as a passive defense mechanism. The conventional method is to execute the delegate Method for releasing resources in the virtual method, so that the derived class can override this method to release their own resources, in addition, the derived class must remember to call the virtual method of the base class version to release the resources related to the base class.
If you have unmanaged resources, your class must have a finalizer method. You should not always trust the client to call the Dispose () method to process resource release. If you forget Dispose () and you do not have the finalizer method, memory leakage may occur.
When GC is running, it removes any spam objects without a terminator (with the finalizer method) from the memory. Objects with Terminator are still in the memory. These objects are added to an end queue, and GC will generate a new thread on these objects to run the Terminator. After the terminator thread completes its work, GC can remove these objects from the memory, so the objects to be terminated will be longer in the memory than the objects not to be terminated. But you don't have to worry about these performance issues.
Implementing IDisposable is a standard method that notifies the user that the system must release the objects holding resources in a timely manner during runtime. The IDisposable interface only contains one method:
Public interface IDisposable
{
Void Dispose ();
}
The implementation of the IDisposable. Dispose () method is for four tasks:
1. Release all unmanaged Resources
2. Release all managed resources (including events not linked)
3. Set a status flag to indicate whether the object is released. You need to check the status. If the object is released again, an ObjectDisposed exception is thrown.
4. Disable the Terminator. Call GC. SuppressFinalize (this) to stop using termination.
To implement IDisposable, you have also completed two tasks. One is to provide a mechanism for the client to release all managed resources in time, and the other is to avoid the client from executing the Terminator.
Simple Example:
public class MyResourceHog : IDisposable{    // Flag for already disposed    private bool alreadyDisposed = false;    // Implementation of IDisposable.    // Call the virtual Dispose method.    // Suppress Finalization.    public void Dispose()    {        Dispose(true);        GC.SuppressFinalize(this);    }    // Virtual Dispose method    protected virtual void Dispose(bool isDisposing)    {        // Don't dispose more than once.        if (alreadyDisposed)            return;        if (isDisposing)        {            // elided: free managed resources here.        }        // elided: free unmanaged resources here.        // Set disposed flag:        alreadyDisposed = true;    }    public void ExampleMethod()    {        if (alreadyDisposed)            throw new ObjectDisposedException(            "MyResourceHog",            "Called Example Method on Disposed object");        // remainder elided.    }}

If an extra cleanup is required for a derived class, it implements the virtual method of the base class as follows:
public class DerivedResourceHog : MyResourceHog{    // Have its own disposed flag.    private bool disposed = false;    protected override void Dispose(bool isDisposing)    {        // Don't dispose more than once.        if (disposed)            return;        if (isDisposing)        {            // TODO: free managed resources here.        }        // TODO: free unmanaged resources here.        // Let the base class free its resources.        // Base class is responsible for calling        // GC.SuppressFinalize( )        base.Dispose(isDisposing);        // Set derived class disposed flag:        disposed = true;    }}

Note that both the base class and the derived class contain the disposed status flag, and the above class does not implement the termination method (because it does not contain unmanaged resources ).
The following code is used by a terminator (destructor:
public class BadClass{    // Store a reference to a global object:    private static readonly List
   
     finalizedList =    new List
    
     ();    private string msg;    public BadClass(string msg)    {        // cache the reference:        msg = (string)msg.Clone();    }    ~BadClass()    {        // Add this object to the list.        // This object is reachable, no        // longer garbage. It's Back!        finalizedList.Add(this);    }}
    
   

At this time, when the class executes the final method, it will put its own reference into a global list, it just to make itself accessible, so it will survive. If you actually need to terminate a restored object, it will not be terminated. GC will not remove any objects that can only be reached through the terminator queue from the memory, but the objects may have been terminated. In this case, these objects are almost certainly no longer used. Although BadClass members are still in the memory, they are like disposed or finalized. There is no way to control the sequence of Terminators. This kind of work will be unreliable.
In a hosted environment, you do not need to create a Terminator. You only need to create a terminator in a class that contains unmanaged resources or a member class that implements IDisposable.


18. Differences between value types and reference types
Which one should you use for the value type, reference type, structure, or class? Not all types in C ++ are defined as value types. This is not JAVA, and all types in JAVA are reference types. When you create a type, it is simple to select the struct or class keyword, but if you change the class later, you will do more work to update client applications that use your class.
This is not a simple option. Correct or not depends on how you use your new type. Value types are not polymorphism, and they are more suitable for storing application operation data. The reference type can be polymorphism and the application can define the behavior of the application.
In C #, you use the struct or class keyword to declare whether the type is a value type or a reference type. The value type should be lightweight and small. Reference types form your class hierarchy.
The following code:
Private MyData myData;
Public MyData Foo ()
{
Return myData;
}
// Call it:
MyData v = Foo ();
TotalSum + = v. Value;
If MyData is of the value type, the returned value is copied and assigned to V.
If MyData is of reference type, you reference an internal variable. This is against encapsulation. The change code is as follows:
Public MyData Foo2 ()
{
Return myData. CreateCopy ();
}
// Call it:
MyData v = Foo ();
TotalSum + = v. Value;
V is a copy of myData. As a reference type, both objects are created on the stack. You don't have to worry about exposing internal data. Instead, you have created an additional object on the stack.
Data types are exported using common methods and attributes.
Consider the following code segment:
Private MyType myType;
Public IMyInterface Foo3 ()
{
Return myType as IMyInterface;
}
// Call it:
IMyInterface iMe = Foo3 ();
IMe. DoWork ();
The myType variable is still returned through the Foo3 method, but does not directly access internal data, but by calling the method of the defined interface. Instead of the data content of the MyType object, you get its behavior (the interface mainly defines the behavior methods of the class ). With this code, we can know the stored values of the value type, and reference the stored behavior of the type.
Now let's take a closer look at how these types are stored in memory and performance-related storage models. Consider the following:
Public class C
{
Private MyType a = new MyType ();
Private MyType B = new MyType ();
// Remaining implementation removed.
}
C cThing = new C ();
How many objects have been created in the code above? How big is each? This depends on the situation. If MyType is a value type, you have allocated a single allocation and allocated two times the size of MyType. If it is a reference type, it has been allocated three times, one is the object C (8 bytes), and the other two are the two MyType objects in c. The result is that the value type is stored in an object inline, but the reference type is not. Each reference type variable contains a reference and requires additional space for storage.
For a thorough understanding, consider the following allocation:
MyType [] arrayOfTypes = new MyType [2, 100];
If MyType is a value type, 100 MyType objects are allocated at one time. However, if the MyType object is of the reference type, it is assigned only once. Each element of the array is null. When you initialize each element of the array, you will perform 101 allocation (101 allocation will take more time than one allocation ). allocating a large number of reference types will cause heap fragmentation and slow down your application. If you create these large types to store data, you should select the value type.
It is important to determine whether to use the value type or reference type. Changing the value type to the class type is a profound change. Consider the following:
public struct Employee{    // Properties elided    public string Position    {        get;        set;    }    public decimal CurrentPayAmount    {        get;        set;    }    public void Pay(BankAccount b)    {        b.Balance += CurrentPayAmount;    }}

This fairly simple class contains a payment method. When the time passes, the system runs better and better, and you decide to have different types of employees: sales staff can get commissions, and managers can get bonuses. If you decide to change the Employee structure to a class:
public class Employee2{    // Properties elided    public string Position    {        get;        set;    }    public decimal CurrentPayAmount    {        get;        set;    }    public virtual void Pay(BankAccount b)    {        b.Balance += CurrentPayAmount;    }}

This will break the code that uses your Employee structure, and the return value is changed to return reference. The parameter is changed from passed value to passed reference. The following code will greatly change:
Employee e1 = Employees. Find (e => e. Position = "CEO ");
BankAccount CEOBankAccount = new BankAccount ();
Decimal Bonus = 10000;
E1.CurrentPayAmount + = Bonus; // Add one time bonus.
E1.Pay (CEOBankAccount );
Now the one-time bonus increase is now a permanent increase. Changing the type affects behavior changes.
Class types can define different implementations of public responsibilities, and the structure should only be used to store data. The value type is more efficient in memory management. It has less heap fragments, less garbage, and less detours. More importantly, when a method or attribute is returned, the Value Type Returns a copy. However, considering scalability, value types have limited support for object-oriented technology. You should consider that all value types are sealed.
If you answer YES to the following questions, you should create a value type.
1. Is the primary responsibility for releasing this type of data storage?
2. is its public interface for accessing data members completely defined by attributes?
3. Are you sure this type will never have child classes?
4. Are you sure this type will never have multiple forms?
Create the underlying data storage use value type, and create the application behavior Use reference type.


19 make sure that 0 is a valid value type status
The default. NET System will set all objects to 0. Because you cannot prevent other languages from using 0 to initialize value types, use 0 as the default value of your type.
A special example is enumeration. Do not create an enumeration type that does not take 0 as the valid state. All enumeration types are inherited from System. ValueType. The default enumeration value starts from 0, but you can modify the default behavior:
public enum Planet{    // Default starts at 0 otherwise.    Mercury = 1,    Venus = 2,    Earth = 3,    Mars = 4,    Jupiter = 5,    Saturn = 6,    Neptune = 7,    Uranus = 8    // First edition included Pluto.}Planet sphere = new Planet();

Sphere is 0, but it is not a valid value. When creating your own Enumeration type, make sure that 0 is a valid value.

20 The type of values that prefer unchanged Atoms
The unchanged types of content are simple. Once created, they are constants. The same type is thread-safe. Multiple Threads can access the same content and will never see inconsistent content. Unchanged content cannot be changed. It works well in the hash-based set. The value returned by Object. GetHashCode () of the unchanged content type is the same. However, it is difficult to change the content of each type. You need to constantly clone new objects to modify their content status. Break down your type into the structure of a natural single entity, that is, the atomic type. A customer class is not an atomic class, because it may contain a lot of information (such as address and phone number), and any independent information may change. An atomic class is a single entity. If you change its composition field, an exception is thrown.
If you need to use the Address class as a struct, keep it unchanged. Any changes to the instance field are set to read-only, as follows:
public struct Address2{    // remaining details elided    public string Line1    {        get;        private set;    }    public string Line2    {        get;        private set;    }    public string City    {        get;        private set;    }    public string State    {        get;        private set;    }    public int ZipCode    {        get;        private set;    }}

Now you have a constant type based on public interfaces. To make it more useful, you need to add the necessary constructor to initialize the complete address structure information. However, remember that struct already has a default constructor. By default, all string type members are Null, and ZIPCode is 0:
public Address2(string line1,string line2,string city,string state,int zipCode) :this(){Line1 = line1;Line2 = line2;City = city;ValidateState(state);State = state;ValidateZip(zipCode);ZipCode = zipCode;}

To use an immutable type, you need to modify its status in a slightly different call sequence. You can modify it by creating a new object instead of modifying an existing instance.
// Create an address:
Address2 a2 = new Address2 ("111 S. Main", "", "Anytown", "IL", 61111 );
// To change, re-initialize:
A2 = new Address2 (a1.Line1, a1.Line2, "Ann Arbor", "MI", 48103 );
Once a new address object is constructed, its value is always fixed. This is safe. If a2 encounters an exception in the construction, it will not affect the original value.
The above Address2 structure is not strictly unchangeable. Private setter can still change the internal data status. The following table lists the truly unchangeable types:
public struct Address3    {        // remaining details elided        public string Line1        {            get { return Line1; }        }        private readonly string line1;        public string Line2        {            get { return line2; }        }        private readonly string line2;        public string City        {            get { return city; }        }        private readonly string city;        public string State        {            get { return state; }        }        private readonly string state;        public int ZipCode        {            get { return zip; }        }        private readonly int zip;        public Address3(string line1,        string line2,        string city,        string state,        int zipCode) :            this()        {            this.line1 = line1;            this.line2 = line2;            this.city = city;            ValidateState(state);            this.state = state;            ValidateZip(zipCode);            this.zip = zipCode;        }    }

The value type does not support derivation, so you cannot expect to use a derived class to modify its fields. However, pay attention to the reference type in the immutable type, which may cause internal data to be modified, thus losing the immutable feature.
For example:
public struct PhoneList{private readonly Phone[] phones;public PhoneList(Phone[] ph){  phones = ph;}public IEnumerable
   
     Phones{  get  {    return phones;  }}}Phone[] phones = new Phone[10];// initialize phonesPhoneList pl = new PhoneList(phones);// Modify the phone list:// also modifies the internals of the (supposedly)// immutable object.phones[5] = Phone.GeneratePhoneNumber();
   

Because the array is a reference type, modifying the element value directly destroys its internal data. To solve this problem, you need to copy a copy of the array.
// Immutable: A copy is made at construction.public struct PhoneList2{private readonly Phone[] phones;public PhoneList2(Phone[] ph){    phones = new Phone[ph.Length];    // Copies values because Phone is a value type.    ph.CopyTo(phones, 0);}public IEnumerable
   
     Phones{  get  {    return phones;  }}}Phone[] phones2 = new Phone[10];// initialize phonesPhoneList p2 = new PhoneList(phones);// Modify the phone list:// Does not modify the copy in pl.phones2[5] = Phone.GeneratePhoneNumber();
   

Non-variable type writing is simple and easy to maintain. Do not blindly create get and set accessors for each attribute. Your first choice for data storage should be an unchangeable atomic value type.

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.