Chapter 4 of C # Series
Iv. Define C # classes
Since the class type is the most important and common type in C #, it is the focus of my introduction. In fact, many features in C # can be reflected by the class type.
As mentioned above, a class object mainly includes fields, attributes, and methods. However, you can also define a nested class or a constant in the class type.
For beginners without any programming knowledge, constants and variables should also be introduced. However, their names can clearly identify the differences between the two. The value of constants cannot be changed, while the value of variables can be modified, unless the variable is read-only (for example, set to readonly ).
The example of the best constant is the circumference rate value, which of course does not change. If you keep the seven digits after the decimal point, the value is 3.1415926. However, if we want to use this value frequently, it is not a good choice to input so many numbers. Moreover, if the user requires a more accurate circumference rate, more decimal places must be reserved, it is very difficult to modify it. Therefore, we need to define a constant:
Const Pi = 3.1415926;
In this case, PI represents 3.1415926. To use the circumference rate, take the PI value directly:
Square = pI * radius;
Note that in the above expression, Pi is a constant. When defining it, the const keyword is used, while square and radius are variables, which are defined as follows:
Double square, radius;
Once pi is defined as a constant, the object of this class cannot be modified after it is created, and the variable can be modified. The followingCodeIs incorrect:
Pi = 3.1415926535897;
Square = pI * radius;
The following two lines of code are correct:
Radius = 2.5;
Square = pI * radius;
The field of the class is actually a variable. For example, the class user in Series 3 contains the m_name, m_password, and m_trycounter fields. Their types are string, string, and Int. The field can still be modified using public, internal, protected, and private. However, we recommend that you do not modify the field to public unless otherwise specified. Because, according to the "Object encapsulation" principle, we should try to avoid providing a type of field to the outside in public mode. After all, for a field, the object has very weak control over it. Once exposed, the caller can easily perform operations on it, especially write operations, which may lead to errors. For example, we add an age field to the user class defined above. If I define it as a public field, as shown below:
Public int age;
The caller may set the age value to a negative number:
User. Age =-5;
The field definition cannot determine such an unreasonable operation, because we cannot control the write operation of the field.
As you can see, the so-called field value corresponds to the attributes of the object mentioned above. For example, name and age are attributes of a user. If the field cannot be set to public, how can the caller access them? The answer is to use the property in the C # class ).
The so-called "attribute" can be viewed as a encapsulation of "field" to a large extent, it uses a GET/set accessor to control the read/write operations on fields separately, and exposes an attribute value, such as the age attribute:
Private int m_age;
Public int age
{
Get {return m_age ;}
Set
{
If (value <0)
{
Throw new argumentoutofrangeexception ("age must be greater than or equal to 0 ");
}
M_age = value;
}
}
In the above Code, the throw statement throws an exception. We can ignore it for the moment, but focus on get and set accessors. First, we define a private field m_age, and then define a public property age. In this attribute, get returns the value of the private field m_age. In set, the value is first determined. If the value is smaller than 0, the value is invalid, it will throw an exception, stop the execution, and tell you that the setting of the age value is incorrect. Of course, we can also set more strict requirements for value values, such as not allowing value to be greater than 150. At least people are not over 150 years old now.
Some people may wonder what the value is? It is actually a keyword provided in C #, which represents the real value you assign to this attribute, for example:
User. Age = 30;
Assign a value to the age attribute.. NET will execute the set accesser, and the value is 30. Then judge whether 30 is less than 0. Apparently the condition is not met, and no exception is thrown. Continue the execution and assign the value 30 to the field m_age. Why do we need to assign it to m_age? Let's take a look at the get accesser. It is actually a read operation. What is the returned value? By the way, the m_age field is shown as follows:
User. Age = 30; // set the field m_age to 30;
Console. writeline ("user's age is {0}.", user. Age); // get the m_age value;
In this case, it will be displayed in the console:
User's age is 30.
In addition, for some special requirements, when we encapsulate a field as an attribute, we can only set its get or set accessors. In this way, this attribute is a read-only attribute, or write only the attributes. This is obviously more conducive to object encapsulation. After all, for a public field, we can control it as read-only (set to readonly), but cannot set it as write-only.
As shown in the preceding figure, the attribute is actually an encapsulation of the field. Through this encapsulation, we can control the read and write operations on the m_age field. At least the current age attribute can control the situation where the value is assigned to a negative number. This is the benefit of attributes.
In C #2.0, apart from setting public and other access modifiers for the entire attribute, you can also set access modifiers for internal get/set accessors, of course, it must be limited. Some restrictions are related to interfaces and rewriting. I will not introduce them for the moment. Here, I will only introduce access modifier conflicts between accessors and attributes.
1. If the entire attribute is set to public, there is no restriction on its accessors;
2. If the entire attribute is set to protected internal, the accessors can only be set to internal, protected, or private;
3. If the entire attribute is set to internal or protected, the access modifier of the accessors can only be private.
For example:
Public Class
{
Private string m_text;
Private int m_count;
Public String text
{
Get {return m_text ;}
Protected set {m_text = value ;}
}
Internal int count
{
Private get {return 5 ;}
Private set {m_count = value}
}
}
SlaveProgramIn essence, attribute is actually a special method, which is equivalent to the following code:
Public int getage ()
{
Return m_age;
}
Public void setage (INT age)
{
M_age = age;
}
In this sense, it is easier to understand the access level modification of the get/set accessors. Essentially, the access level modification of the so-called accessors is nothing more than the access level modification of methods. Of course, the properties provided in C # are easier than the get/Set Method for accessing fields. In general, to define a method, it should be related to the behavior of an object, such as the signin () and signout () methods in the user class defined in Series 3, they represent the behavior of the Object User: Login and exit.
A method that defines a class must contain five elements: Method modifier, method name, return type, parameter, and method body, such as the add method:
Public int add (int x, int y)
{
Return X + Y;
}
Public is our method modifier, which represents the level at which the method can be accessed. Of course, the keyword of the modifier method also includes static, virtual, abstract, and so on, but this content will be introduced later. The method name is "add", which is the name of the method. If the return type is int, a result is returned for this method. The return type is int. There are two parameters, X and Y, respectively, whose types are Int. The caller can pass values to the method body through parameters and operate on them. The method body is the content in curly brackets.
Assuming that the add method is defined in the calculator class, the method is called as follows:
Calculator Cal = new calculator ();
Int result = Cal. Add (3, 5 );
Call add, input parameters 3 and 5, and get result 8, and return. Therefore, the value of the result variable is 8. The first line of code is to use the new keyword to instantiate the calculator class and obtain an object Cal. You can use the object Cal to call the public methods, attributes, or fields of the calculator class.
Why do we need to instantiate it? We define a class type, which is used by the caller, otherwise it will lose its meaning. However, the class type we define only represents a certain format. For example, the user class indicates that it is a class and has some fields, attributes, and methods. With this definition,. Net can recognize these types of objects. If you really want to call these types of objects, you must "instantiate" them. This operation will create objects one by one during runtime and put them in the memory space for the program to call. Just like a "person" is a class type, and a specific person is a real and instantiated object. To instantiate a class type, you must provide a "Constructor" for this type ". The constructor is a special method that does not return a type, and its method name and type name are consistent, such as the definition of the calculator class and its constructor:
Public class Calculator
{
Public calculator ()
{
}
Public int add (int x, int y)
{
Return X + Y;
}
}
The calculator () method is a constructor. This constructor has no parameters. in C #, it is also called the default constructor, even if it is not defined ,. net will also be created for it by default. For example, in the calculator class, we can delete the definition of the calculator () constructor. However, if we define a constructor with parameters, the default constructor will not exist. If we need to create an instance without parameters, you need to create the constructor explicitly. For example, the previous user class. If the name and password are a very important attribute of this class, in most cases, if you want to create a user object, when the values of these two attributes are required, we can create a special constructor for the user class:
Public class user
{
Public user (string name, string password)
{
M_name = Name;
M_password = password;
}
}
Note that the constructor receives two parameters: name and password, and assigns the value to the m_name and m_password fields of the user class. Therefore, when we create an instance of the user class in the following way, the object we create has the value of name and password:
User specuser = new user ("Bruce Zhang", "password ");
However, if the following method is used to create a user instance, an error occurs:
User user = new user ();
The default constructor of the user class (that is, the constructor without parameters) does not exist. To support the above instantiation method, you need to add a non-argument constructor in the definition of the user class.
Whether to define a constructor with parameters for a class depends on the specific needs. For the user class, since the name and password attributes of the class are essential to an object, it is necessary to create such a constructor. If this constructor is not available, the following three lines of code are required for the previously constructed specuser:
User specuser = new user ();
Specuser. Name = "Bruce Zhang ";
Specuser. Password = "password ";
Note: In the definition of a class, we can use the this keyword to represent its class object. Through this, we can access all constants, fields, attributes, and methods of this class, whether it is public or private, or other access levels. Although this refers to the class object itself, that is to say, it represents the object produced by instantiation, but the meaning of this is only limited to the internal of the object. From the perspective of object encapsulation, it is encapsulated and cannot be seen outside the object.
For example, the following definition:
Public class visitor
{
Public void visit (Element E)
{
Console. writeline ("I was visited .");
}
}
Public class Element
{
Public void accept (visitor V)
{
V. Visit (this );
}
}
In the element class, the accept method imports a value of the visitor type. In this method, the visit method of the parameter V is called, while the visit method imports the value of the element type, because the accept method itself belongs to the element class, we can pass itself to the visit method, that is, this in the code.
Analyze the following code snippet:
Visitor v = new visitor ();
Element E = new element ();
E. Accept (v );
Instance E of element, and the accept () method is executed. The input parameter of this method is instance V of the visitor class. Then, the accept method is actually executed within the method v. Visit () method, and the Object E itself is passed through the this keyword, so the final result is to print the following string:
I was visited.
The naming requirements are mentioned here by the way. Naming Conventions are very important when being developed as a team. Taking this article as an example, how to define the class name, field name, attribute name, and method name are all exquisite. Generally, the class name, attribute name, and method name must be capitalized on the first letter of all words. If it is a field, unless it is a public field, in general, the first letter of the first word should be lowercase. However, this is also the requirement for variable naming. Some variables, rather than fields, may be used temporarily in a class. to distinguish between common variables and fields, C ++ programmers like to add the "_" symbol before the variable name, many C # programmers also follow this habit. However, I prefer to add "M _" before these field names _". Naming must be unified, especially in a team. However, such temporary variables or non-public variables have fewer restrictions on names. After all, these variables are not used by class callers. In addition, for constants, it is recommended to define all uppercase names, such as the previously defined constant pi.
C # There is a complete set of naming rules. If you are interested, you can check the Special Materials on your own. In addition, different companies may have some specific naming rules, so we will not go into detail here.