Improve the C # program's 50 methods. Clause 6: Use Cases for identifying value types and reference types

Source: Internet
Author: User

Value Type or reference type? Structure or class? How to use them correctly? Here it is not c ++, where all types are defined as value types, and then we can choose to create their reference form. This is not Java, where all types are reference types [9]. In C #, we must determine the behavior of a type instance when designing the type. This decision is very important. We must be clear about the consequences of this decision, because later changes may cause many code errors inadvertently. It may be easy to select the struct or class keyword when creating a type, but if you want to change it later, all client programs that use our type will make many changes accordingly.

If the class is better than the struct or struct is better than the class, the problem may be simplified. The correct choice depends on how we expect future customer programs to use our types. The value type does not support polymorphism and is suitable for storing data operated by the supplied program. The reference type supports polymorphism and should be used to define the behavior of an application. When designing a type, we should consider the type responsibility. Based on the expected responsibility, we can determine the type of creation. In short, structures are used to store data and classes are used to define behaviors.

Due to some common problems in C ++ and Java,. NET and C # introduce the differences between value types and reference types. In C ++, all parameters and return values are passed by passing values. The method of transferring values is very efficient, but it brings about a problem: incomplete copying (also called object cutting ). If the object of a derived class is transferred where the base class object is required, only the part of the object of the derived class that belongs to the base class will be copied. Therefore, we will lose information about this derived class. The called virtual function is also used as the base class version.

 

Java solves this problem by abandoning the value type to some extent. All user-defined types in Java are reference types. All parameters and return values are passed as references [10]. The benefit of this strategy is consistency, but there is a certain loss in performance. In fact, some types do not need to support polymorphism. Java programmers must pay for heap memory allocation and garbage collection for each variable [11]. In addition, it takes some additional time to perform "dereference" for each variable. In the final analysis, all variables are of reference type. In C #, we use the struct or class keyword to declare whether a type is a value type or a reference type. Value types are mainly used for lightweight data types, while reference types are mainly used to build the entire class hierarchy ). This section describes how to use different types to help you understand the differences between value types and reference types.

Let's start with the following code. Here we use the type as the return value of a method:

Private mydata _ mydata;

Public mydata Foo ()

{

Return _ mydata;

}

// Call the Foo () method:

Mydata v = Foo ();

Totalsum + = V. value;

If mydata is a value type, the returned value is copied to the storage space of V, where v is on the stack. However, if mydata is a reference type, we actually expose the reference of an internal variable to the outside world. This breaks the type encapsulation principle (see section 23 ).

If the above Code is changed as follows:

Private mydata _ mydata;

Public mydata Foo ()

{

Return _ mydata. Clone () as mydata;

}

// Call the Foo () method:

Mydata v = Foo ();

Totalsum + = V. value;

Now, V is a copy of _ mydata. As a reference class type, _ mydata and V are both on the stack. This avoids exposing internal data of the type to the outside world, but creates additional objects on the stack. If V is a local variable, it will soon become garbage, and the clone method also forces us to perform a runtime type check. In general, this approach is not efficient enough.

Therefore, the data type exposed to the outside world through public methods and attributes should be value type. Of course, this does not mean that each public member returns a value type. The above Code actually has a hypothesis for mydata, that is, it is responsible for storing data.

However, consider the following code:

Private mytype _ mytype;

Public imyinterface Foo ()

{

Return _ mytype as imyinterface;

}

// Call the Foo () method:

Imyinterface IME = Foo ();

IME. dowork ();

In the above Code, _ mytype is still returned from the foo method. But this time, instead of accessing the internal data of the returned value, a method is called through a defined interface. The purpose of this access to the mytype object is not the data content, but its behavior-the above Code uses imyinterface to express the behavior, and its implementation can use a variety of different types. In this example, we use the reference type instead of the value type. The responsibility of mytype lies in its behaviors rather than its data members.

This simple code shows the difference between the value type and the reference type: the value type is used to store data, and the reference type is used to define behavior. Let's take a further look at how these types are stored in the memory and the performance considerations related to the storage model. Consider the following classes:

Public Class C

{

Private mytype _ A = new mytype ();

Private mytype _ B = new mytype ();

// Skip Other implementations.

}

C Var = new C ();

How many objects have been created in the above Code? How big are they? This depends on the situation. If mytype is a value type, only one memory allocation is required. The size is twice the size of mytype [12]. However, if mytype is a reference type, there will be three memory allocations: one for the c object, the size is 8 bytes (assuming the pointer is 32 characters ); it is used twice for the allocation of mytype objects contained in Object C. The reason for different results is that the value type is stored in the object in inline mode, while the reference type is not. Each variable of the reference type stores only one reference and requires additional allocation in the bucket.

To help you understand this, consider the following code:

Mytype [] Var = new mytype [100];

If mytype is a value type, you only need to allocate a configuration at a time. The size is 100 times the size of the mytype object. However, if mytype is a reference type, an allocation is required at the beginning, and the element values of the allocated array are null. If you initialize each element in the array, we will need to execute a total of 101 allocation times-101 allocation takes more time than one allocation. Allocating many reference type objects will cause many fragments in the heap space, thus reducing the system speed. If the type we create is mainly used to store data, the value type is undoubtedly the best choice.

Designing a type as a value type or a reference type is a very important decision. If it is not determined at the beginning, changing the value type to the reference type will have a lot of impact. Consider the following types:

Public struct employee

{

Private string _ name;

Private int _ id;

Private decimal _ salary;

// All attributes are omitted.

Public void pay (bankaccount B)

{

B. Balance + = _ salary;

}

}

The above example is quite simple and only contains one method for paying salaries to the employee. The system is running well at the beginning, but as time goes on, we may need different types of employees: sales staff can get proxy commission, and managers can get bonus bonuses. Therefore, we need to change employee to a class:

Public class employee

{

Private string _ name;

Private int _ id;

Private decimal _ salary;

// All attributes are omitted.

Public Virtual void pay (bankaccount B)

{

B. Balance + = _ salary;

}

}

This will corrupt a lot of code that is currently using the original employee structure. The original "return by value" will be changed to "return by reference ". The original "Pass Parameter" will be changed to "Pass reference parameter ". For example, the behavior of the following small piece of code will change a lot:

Employee e1 = employees. Find ("CEO ");

E1.salary + = bonus; // Add a bonus.

E1.pay (ceobankaccount );

If the employee is a structure, the meaning of this code is "one-time bonus ". But now I want to change the employee to a class. This Code expresses a "permanent salary increase ". Where "copy by value" was originally applied, it is now replaced with "copy by reference ". The compiler will happily help us with this change, and the CEO may also be happy. However, CFO may report bugs. Changing the value type to the reference type will lead to changes in program behavior, so we cannot do this.

The reason for the above problem is that the employee type no longer complies with the value type design principles. In addition to storing the employee data, we also add a responsibility for it-in this example, it is to pay the salary to the employee. Responsibility is the category of the class. Class can easily define the polymorphism implementation of common responsibilities. Structures cannot do this. They should be limited to data storage.

In the. NET document, we recommend that you determine whether to select a value type or a reference type. In reality, a more important factor worth consideration should be the type of use cases. If it has a simple structure or is used as a data carrier, it is more suitable for designing as a value type. Value types have better efficiency in memory management: less heap memory fragments, less memory garbage, and less indirect access. More importantly, the value type is returned from a method or attribute in a copy-This avoids exposing the reference of the internal structure to the outside world. However, the value type does not provide certain features. Value types have limited support for common object-oriented technologies. We cannot create an inheritance level for the value type. We should treat all value types as sealed types. We can enable the value type to implement some interfaces, but the packing operation is required. Article 17 shows the performance problems caused by it. We should regard the value type as the storage container for data, rather than the objects in the OO (Object-Oriented) environment.

Generally, the reference types we create always have more than one ratio type. If the answer to the following questions is yes, we should create a value type. You can apply these questions to the preceding employee example:

1. is the primary role of this type used for data storage?

2. Is this type of public interface completely defined by some data member access attributes?

3. Are you sure this type can never be a subclass?

4. Are you sure this type will never have polymorphism?

To sum up, we should design the types used for underlying data storage as value types, and design the types used to define application behavior as reference types. In this way, we have gained the security of copying data from class objects, as well as the memory advantages brought by stack-based and inline storage models, at the same time, we can also use standard object-oriented technology to create application logic. If you are not sure about future applications of the type, you should use the reference 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.