C # create constant and atomic value types

Source: Internet
Author: User

SlaveType Design
From Class to Struct
If we want to design a Type that stores the recipient's Address, we call this Type Address. It should contain the following attributes:

Province
City
Zip code

We need to control the Zip format (all numbers and 6 digits are required). How should we design it? I think many people will write it like this:

Public class Address {
Private string province;
Private string city;
Private string zip;

Public string Province {
Get {return province ;}
Set {province = value ;}
}

Public string City {
Get {return city ;}
Set {city = value ;}
}

Public string Zip {
Get {return zip ;}
Set {
CheckZip (value); // verify the format
Zip = value;
}
}

// Check whether the zip file is correct
Private void CheckZip (string value ){
String pattern = @ "d {6 }";
If (! Regex. IsMatch (value, pattern ))
Throw new Exception ("Zip is invalid! ");
}
Public override string ToString (){
Return String. Format ("Province: {0}, City: {1}, Zip: {2}", province, city, zip );
}
}

The first problem already exists: When we declare a class, we define a series of related operations (or actions and methods). Of course, the class also contains fields and attributes, however, these fields are usually used by the class method, and attributes are often used to indicate the status of the class (such as the Length of StringBuilder), the ability of the class (such as the Capacity of StringBuilder ), the status or stage of the method. When defining a structure, we usually only use it to store data, instead of providing methods, or simply providing methods to operate or convert itself, instead of providing services for other types.

Address does not contain any method. It only organizes three data, namely Provice, City, and Zip, into an independent individual, so it is best to declare it as a Struct rather than a Class. (This is also an exception: If the Address contains 20 or more fields, consider declaring it as a Class because the Class is passed as a reference when passing parameters, struct is used to transmit values. When the data size is small, the data transmission efficiency is higher. When the data size is large, the data transmission and reference occupy less memory space .)

So we can first declare the Address as a Struct instead of a Class.

Data inconsistency
Next we will use the Address type we just created:

Address a = new Address ();
A. Province = "Shaanxi ";
A. City = "Xi'an ";
A. Zip = "710068 ";
Console. WriteLine (a. ToString (); // Province: Shaanxi, City: Xi'an, Zip: 710068

It seems that there is no problem, but in retrospect, the type definition may throw an exception when assigning values to the Zip attribute, so we should put it in a Try Catch statement, at the same time, we will assign an error value to Zip to see what will happen:

Try {
A. City = "Qingdao ";
A. Zip = "12345"; // an exception is triggered here
A. Province = "Shandong ";
} Catch {
}
Console. WriteLine (a. ToString (); // Province: Shaanxi, City: Qingdao, Zip: 710068

The result is a data inconsistency problem. When assigning a value to Zip, the assignment to Zip and the subsequent Province fails because of an exception, however, the City value is successfully assigned. The result is that Provice is located in Shaanxi, while City is located in Qingdao.

That is, when the Zip value is assigned, no exception is thrown, and a problem occurs: in the case of multithreading, when the current thread executes to change the City to "Qingdao ", however, when Zip and Province are not modified yet (Zip is still "710068" and Province is still "Shaanxi "). If other threads access instance a at this time, the inconsistent data will also be read.

Constants and atomicity
Now that we know the problem above, how can we improve it? Let's take a look at the author's definition of constants and atomicity:

Atomicity of an object: The state of an object is a whole. If a field changes, other fields must be changed at the same time. Simply put, it is either not modified or all changed.
Object constants: Once the object State is determined, it cannot be changed again. If you want to change it again, you need to reconstruct an object.
We already know the atomic and constant concepts of objects. How can we implement them next? For atomicity, we add a constructor to assign values to all fields of the object in this constructor. To implement constants, we cannot modify the object state after assigning values to the object. Therefore, we delete the set accessors In the attribute and declare the fields as readonly:

Public struct Address {
Private readonly string province;
Private readonly string city;
Private readonly string zip;

Public Address (string province, string city, string zip ){
This. city = city;
This. province = province;
This.zip = zip;
CheckZip (zip); // verify the format
}

Public string Province {
Get {return province ;}
}

Public string City {
Get {return city ;}
}

Public string Zip {
Get {return zip ;}
}
// Others...
}

In this way, we create an Address object and assign values to all fields in the constructor as a whole. When we need to change the value of a single field, you also need to recreate the object and assign a value. Let's take a look at the following test:

Address a = new Address ("Shaanxi", "Xi'an", "710068 ");

Try {
A = new Address ("Qingdao", "Shandong", "22233"); // if an exception occurs, the assignment to a fails, but the status remains the same
} Catch {
}

Console. WriteLine (a. ToString (); // output: Province: Shaanxi, City: Xi'an, Zip: 710068

Avoid external access to internal types
The above method solves the data inconsistency problem, but it also misses a point: when the type maintains a reference type field, such as an array. Although we declare it as readonly, it can still be accessed outside the type (if you do not know the difference between the value type and the reference type, refer to C # type basics ). Now let's modify the Address class and add an array of phones to store the phone number:

Private readonly string [] phones;

Public Address (string province, string city, string zip, string [] phones ){
// Omitted...
This. phones = phones;
}

Public string [] Phones {
Get {return phones ;}
}

Let's perform a test:

String [] phones = {"029-88401100", "029-88500321 "};
Address a = new Address ("Shaanxi", "Xi'an", "710068", phones );

Console. WriteLine (a. Phones [0]); // output: 029-88401100

String [] B = a. Phones;
B [0] = "029-XXXXXXXX"; // The Address content is modified through B.

Console. WriteLine (a. Phones [0]); // output: 029-XXXXXXXX

Although the phones field is declared as readonly, only the get attribute accessors are provided. We can still modify the internal content of object a through the variable B outside Address object. How can this problem be avoided? We can solve this problem through deep replication. Add the following code to the get attribute accessors of Phones:

Public string [] Phones {
Get {

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.