Negative tive C # Principle 6: difference value type data and reference type data

Source: Internet
Author: User

Negative tive C # Principle 6: difference value type data and reference type data
Item 6: distinguish between value types and reference types

Value Type data or reference type data? Structure or class? What do you need to use them? This is not c ++. You can define all types as value types and make a reference for them. This is not Java, and all types are value types. When creating each type of instances, you must determine the form in which they exist. This is an important decision that must be taken at the beginning to get the correct results. You must always face the consequences of this decision, because you want to modify it later, you have to forcibly add a lotCode. When you design a type, it is easy to select struct or class. However, once your type is changed, updating all users of this type requires a lot of effort (compared with the design time.

This is not a simple alternative. The correct choice depends on how you want your new type to be used. Value types do not have polymorphism, but they are in your applicationProgramData can be accessed with good performance. The reference type can have polymorphism, and you can also define some behavior for them in your application. Consider what type of functions you want to design for you, and decide what type to design based on these functions. Structure stores data, and class represents behavior.

Because many common problems exist in C ++ and javaj, the. NET and C # pairs distinguish between value types and reference types. In C ++, all parameters and return values are passed as values. It is very efficient to pass the object as a value type, but it has to bear the problem that partial copying (also known as slicing object) is an object ). If you copy data to a derived object in the form of a base class, only part of the data of the base class is copied. You directly lose all the information of the derived object. Even if you use a virtual function of the base class.

The Java language, after giving up value-type data, is more or less a bit of performance. In javs, all user-defined types are reference types, and all parameters and returned data are transmitted as reference types. This policy has its advantages in (data) consistency, but its performance is flawed. Let us face this situation, some types are not polymorphism-they are not needed. Java programmers have prepared a memory heap distributor and a final Garbage Collector for all the variables. They also need to spend extra time accessing each referenced variable because all variables are of reference type. In C #, you can declare a value type data with struct, or declare a reference type data with class. The value type data should be small and lightweight. The reference type is inherited from your class. This section describes how to use a data type in different ways, so that you can understand the differences between value-type data and reference-type data.

At the beginning, there is a type returned from a method:

Private mydata _ mydata;
Public mydata Foo ()
{
Return _ mydata;
}
// Call it:
Mydata v = Foo ();
Totalsum + = V. value;

If mydata is a value type, the return value is copied to v. And V is on the stack memory. However, if mydata is a reference type, you have imported a reference to an internal variable. At the same time,
You also violate the encapsulation principle (see principle 23 ).

Or consider this variable:

Private mydata _ mydata;
Public mydata Foo ()
{
Return _ mydata. Clone () as mydata;
}

// Call it:
Mydata v = Foo ();
Totalsum + = V. value;

Now, V is a copy of the raw data _ mydata. As a reference type, both objects are created on the memory stack. You don't have to worry about exposing internal data. Instead, you will create an additional data object on the stack. If V is a local variable, it will soon become junk, and Clone requires you to perform type detection at runtime. All in all, this is inefficient.

Data exposed by public methods or attributes should be of the value type. However, this does not mean that all types returned from public members must be value types. Assume that mydata has data, and its responsibility is to save the data.

However, you can consider the following code snippet:
Private mytype _ mytype;
Public imyinterface Foo ()
{
Return _ mytype as imyinterface;
}

// Call it:
Imyinterface IME = Foo ();
IME. dowork ();

The Variable _ mytype is returned from the foo method. But this time the difference is that, instead of accessing the internal data of the returned value, you can access the object by calling a method on the defined interface. You are accessing a mytype object, not its specific data, but its behavior. This behavior is presented to us by the imyinterface. At the same time, this interface can be implemented by many other types. In this example, mytype is a reference type instead of a value type. The responsibility of mytype is to consider its behavior, not its data members.

This simple code tells you the difference: the value type stores data and the reference type represents behavior. Now let's take a deeper look at how these types are stored in the memory and the performance of the storage model. Consider the following class:

Public Class C
{
Private mytype _ A = new mytype ();
Private mytype _ B = new mytype ();

// Remaining implementation removed.
}

C Var = new C ();

How many objects have been created? How much memory does it occupy? This is hard to say. If mytype is a value type, you only make one heap memory allocation. The size is exactly twice the size of mytype. However, if mytype is a reference type, you have made three heap memory allocations: one is a C object, which occupies 8 bytes (assuming you use a 32-bit pointer) the heap memory is allocated to the mytype object contained in the C object twice. The reason for this difference is that the value type exists in an object in an inline way. On the contrary, the reference type is not. Each reference type only retains one reference pointer, and data storage requires additional space.
To understand this, consider the following memory allocation:

Mytype [] Var = new mytype [100];

If mytype is a value type data, 100 mytype space will be allocated at a time. However, if mytype is a reference type, only one memory allocation is required. Every data element is null. When you initialize each element in the array, You need to perform 101 allocation tasks-and the memory ratio of the 101 allocation takes more time. Allocating a large amount of reference data can cause fragmentation of heap memory and thus reduce program performance. If the type you created is intended to store the value of the data, you need to select the value type.

Whether to use value-type data or reference type data is a very important decision. Transforming a value type data into a class is a profound change. Consider the following situation:

Public struct employee
{
Private string _ name;
Private int _ id;
Private decimal _ salary;

// Properties elided

Public void pay (bankaccount B)
{
B. Balance + = _ salary;
}
}

This is a clear example. This type contains a method that you can use to pay salaries for your employees. As time passes, your system is running fairly. Next, you decided to grade different employees: the sales staff received the bonus and the manager got the bonus. You decided to change the employee type to a class:

Public class employee
{
Private string _ name;
Private int _ id;
Private decimal _ salary;

// Properties elided

Public Virtual void pay (bankaccount B)
{
B. Balance + = _ salary;
}
}

This disrupts a lot of code that already exists and uses the structure you designed. The Return Value Type changes to the return reference type. The parameter is also transferred from the original value to the current reference. The behavior of the following code segment will be severely affected:

Employee e1 = employees. Find ("CEO ");
E1.salary + = bonus; // Add one time bonus.
E1.pay (ceobankaccount );

This one-time bonus addition operation has become a continuous improvement. Once a copy of the value type, it is now a reference of the reference type. The compiler is happy to make this change for you, and your CEO is happy to make it. On the other hand, your CEO will report bugs to you.
You still failed to change your opinion on the Value Type and reference type, so that you do not know that the mistake has changed your behavior!

This problem occurs because the employee no longer complies with the value-type data principle.
In addition, it is defined as an element for saving data for empolyee. In this example, you must add a role for it: pay wages for employees. The responsibility is within the scope of the class. Classes can be defined with polymorphism, so it is easy to implement some common responsibilities; while the structure is not sufficient, it should be limited to saving data.

When selecting between the value type and the reference type, we recommend that you consider the size of the type as a deciding factor in the. NET instruction document. In fact, more factors are the type of use. A simple structure or simple data carrier is an excellent candidate for value-type data. It turns out that value-type data has good performance in memory management: they have very few heap memory fragments, few are generated by garbage, and few are accessed indirectly.
The garbage here and the garbage mentioned above refer to the objects that are "dead" in the heap memory and cannot be accessed by users. They only wait for the objects to be collected by the garbage collector, it is considered as garbage. In. net, garbage objects are usually used. We recommend that you take a look at the management model of the Garbage Collector under. net)
More importantly, when a method or attribute is returned, the value type is copy data. This is not a risk of exposing the internal structure. But you pay in terms of features. value types have limited support in object-oriented technology. You should regard all value types as closed. You can create a value type that implements the interface, but this requires packing. Principle 17 will explain to you that this will cause performance loss. Consider the value type as a data container. It is no longer an object in OO.

You may create more reference types than each other. If you answer yes to all of the following questions, you should create value-type data. Compare the following problem with the previous example of employee:

1. Is data storage the most basic responsibility of data type?
2. Does its attribute have a complete public interface to access or modify data members?
3. Will I never be confident in sub-categories of types?
4. Will I never be too confident about the type?

The value type is treated as a low-level data storage type, and the behavior of the application is represented by the reference type.
You will obtain the copy of the security data from the method exposed by the class. You will get the advantage of high memory usage from the value type that uses inline. In addition, you can use standard object-oriented technology to create application logic. When you are not sure about the expected use, use the reference type.

======================================
Summary: This principle is a little long and takes a little more time. I wanted to finish it in two or three hours after work, because I have translated some of it yesterday, or accidentally reached.
Finally, this principle does not indicate what is the reference type or the value type. Of course, the class type must be the reference type, and struct indicates the value type. Pay attention to the properties of other types: for example, what type is enumeration? What is the delegate type? What about events?

Item 6: distinguish between value types and reference types
value types or reference types? Structs or classes? When should you use each? This isn't c ++, in which you define all types as value types and can create references to them. this isn' t Java, in which everything is a reference type. you must decide how all instances of your type will behave when you create it. it's an important demo-to get right the first time. you must live with the consequences of your demo-because changing later can cause quite a bit of code to break in subtle ways. it's a simple matter of choosing the struct or class keyword when you create the type, but it's much more work to update all the clients using your type if you change it later.

it's not as simple as preferring one over the other. the right choice depends on how you have CT to use the new type. value types are not polymorphic. they are better suited to storing the data that your application manipulates. reference types can be polymorphic and shocould be used to define the behavior of your application. consider the expected responsibilities of your new type, and from those responsibilities, decide which type to create. structs store data. classes define behavior.

the distinction between value types and reference types was added. net and C # Because of common problems that occurred in C ++ and Java. in C ++, all parameters and return values were passed by value. passing by value is very efficient, but it suffers from one problem: Partial copying (sometimes called slicing the object ). if you use a derived object where a base object is expected, only the base portion of the object gets copied. you have specified tively lost all knowledge that a derived object was ever there. even callto virtual functions are sent to the base class version.

The Java language responded by more or less removing value types from the language. all user-defined types are reference types. in the javalanguage, all parameters and return values are passed by reference. this strategy has the advantage of being consistent, but it's a drain on performance. let's face it, some types are not polymorphicthey were not intended to be. java programmers pay a heap allocation and an eventual garbage collection for every variable. they also pay an extra time cost to dereference every variable. all variables are references. in C #, you declare whether a new type shocould be a value type or a reference type using the struct or class keywords. value types shoshould be small, lightweight types. reference types form your class hierarchy. this section examines different uses for a type so that you understand all the distinctions between value types and reference types.

To start, this type is used as the return value from a method:

Private mydata _ mydata;
Public mydata Foo ()
{
Return _ mydata;
}

// Call it:
Mydata v = Foo ();
Totalsum + = V. value;

 

If mydata is a value type, the return value gets copied into the storage for v. furthermore, V is on the stack. however, if mydata is a reference type, you 've exported a reference to an internal variable. you 've violated the principal of encapsulation (see item 23 ).

Or, consider this variant:

Private mydata _ mydata;
Public mydata Foo ()
{
Return _ mydata. Clone () as mydata;
}

// Call it:
Mydata v = Foo ();
Totalsum + = V. value;

 

Now, V is a copy of the original _ mydata. as a reference type, two objects are created on the heap. you don't have the problem of exposing internal data. instead, you 've created an extra object on the heap. if V is a local variable, it quickly becomes garbage and clone forces you to use runtime type checking. all in all, it's inefficient.

Types that are used to export data through public methods and properties shocould be value types. but that's not to say that every type returned from a public member shocould be a value type. there was an assumption in the earlier code snippet that mydata stores values. its responsibility is to store those values.

But, consider this alternative code snippet:

Private mytype _ mytype;
Public imyinterface Foo ()
{
Return _ mytype as imyinterface;
}

// Call it:
Imyinterface IME = Foo ();
IME. dowork ();

 

The _ mytype variable is still returned from the foo method. but this time, instead of accessing the data inside the returned value, the object is accessed to invoke a method through a defined interface. you're accessing the mytype object not for its data contents, but for its behavior. that behavior is expressed through the imyinterface, which can be implemented by multiple different types. for this example, mytype shocould be a reference type, not a value type. mytype's responsibilities revolve around its behavior, not its data members.

That simple code snippet starts to show you the distinction: value types store values, and reference types define behavior. now look a little deeper at how those types are stored in memory and the performance considerations related to the storage models. consider this class:

Public Class C
{
Private mytype _ A = new mytype ();
Private mytype _ B = new mytype ();

// Remaining implementation removed.
}

C Var = new C ();

 

How many objects are created? How big are they? It depends. if mytype is a value type, you 've made one allocation. the size of that allocation is twice the size of mytype. however, if mytype is a reference type, you 've made three allocations: one for the c object, which is 8 bytes (assuming 32-bit pointers ), and two more for each of the mytype objects that are contained in a C object. the difference results because value types are stored inline in an object, whereas reference types are not. each variable of a reference type holds a reference, and the storage requires extra allocation.

To drive this point home, consider this allocation:

Mytype [] Var = new mytype [100];

 

If mytype is a value type, one allocation of 100 times the size of a mytype object occurs. however, if mytype is a reference type, one allocation just occurred. every element of the array is null. when you initialize each element in the array, you will have timed med 101 allocationsand 101 allocations take more time than 1 allocation. allocating a large number of reference types fragments the heap and slows you down. if you are creating types that are meant to store data values, value types are the way to go.

The demo-to make a value type or a reference type is an important one. It is a far-reaching change to turn a value type into a class type. Consider this type:

Public struct employee
{
Private string _ name;
Private int _ id;
Private decimal _ salary;

// Properties elided

Public void pay (bankaccount B)
{
B. Balance + = _ salary;
}
}

 

This fairly simple type contains one method to let you pay your employees. time passes, and the system runs fairly well. then you decide that there are different classes of employees: salespeople get commissions, and managers get bonuses. you decide to change the employee type into a class:

Public class employee
{
Private string _ name;
Private int _ id;
Private decimal _ salary;

// Properties elided

Public Virtual void pay (bankaccount B)
{
B. Balance + = _ salary;
}
}

 

That breaks much of the existing code that uses your customer struct. return by value becomes return by reference. parameters that were passed by value are now passed by reference. the behavior of this little snippet changed drastically:

Employee e1 = employees. Find ("CEO ");
E1.salary + = bonus; // Add one time bonus.
E1.pay (ceobankaccount );

 

What was a one-time bump in pay to add a bonus just became a permanent raise. where a copy by value had been used, a reference is now in place. the compiler happily makes the changes for you. the CEO is probably happy, too. the CFO, on the other hand, will report the bug. you just can't change your mind about value and reference types after the fact: it changes behavior.

This problem occurred because the employee type no longer follow the guidelines for a value type. in addition to storing the data elements that define an employee, you 've added responsibilitiesin this example, paying the employee. responsibilities are the domain of class types. classes can define polymorphic implementations of common responsibilities easily; structs cannot and shoshould be limited to storing values.

The documentation. net recommends that you consider the size of a type as a determining factor between value types and reference types. in reality, a much better factor is the use of the type. types that are simple structures or data carriers are excellent candidates for value types. it's true that value types are more efficient in terms of Memory Management: there is less heap fragmentation, less garbage, and less indirection. more important, value types are copied when they are returned from methods or properties. there is no danger of exposing references to internal structures. but you pay in terms of features. value types have very limited support for common object-oriented techniques. you cannot create object hierarchies of value types. you shoshould consider all value types as though they were sealed. you can create value types that implement interfaces, but that requires boxing, which item 17 shows causes performance degradation. think of value types as storage containers, not objects in the OO sense.

You'll create more reference types than value types. If you answer yes to all these questions, you shoshould create a value type. Compare these to the previous employee example:

Is this type's principal responsibility data storage?

Is its public interface defined entirely by properties that access or modify its data members?

Am I confident that this type will never have subclasses?

Am I confident that this type will never be treated polymorphically?

Build low-level data storage types as value types. build the behavior of your application using reference types. you get the safety of copying data that gets exported from your class objects. you get the memory usage benefits that come with stack-based and inline value storage, and you can utilize standard object-oriented techniques to create the logic of your application. when in doubt about the expected use, use a 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.