青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 195,  comments - 30,  trackbacks - 0

C# Concepts: Value vs Reference Types

Joseph Albahari

Introduction

One area likely to cause confusion for those coming from a Java or VB6 background is the distinction between value types and reference types in C#. In particular, C# provides two types—class and struct, which are almost the same except that one is a reference type while the other is a value type. This article explores their essential differences, and the practical implications when programming in C#.

This article assumes you have a basic knowledge of C#, and are able to define classes and properties.

First, What Are Structs?

Put simply, structs are cut-down classes. Imagine classes that don’t support inheritance or finalizers, and you have the cut-down version: the struct. Structs are defined in the same way as classes (except with the struct keyword), and apart from the limitations just described, structs can have the same rich members, including fields, methods, properties and operators. Here’s a simple struct declaration:

struct Point
{
   private int x, y;             // private fields

 

   public Point (int x, int y)   // constructor
  
{
         this.x = x;
         this.y = y;
   }

   public int X                  // property
  
{
         get {return x;}
         set {x = value;}
   }

   public int Y
  {
         get {return y;}
         set {y = value;}
   }
}    

Value and Reference Types

There is another difference between structs and classes, and this is also the most important to understand. Structs are value types, while classes are reference types, and the runtime deals with the two in different ways. When a value-type instance is created, a single space in memory is allocated to store the value. Primitive types such as int, float, bool and char are also value types, and work in the same way. When the runtime deals with a value type, it's dealing directly with its underlying data and this can be very efficient, particularly with primitive types.

With reference types, however, an object is created in memory, and then handled through a separate reference—rather like a pointer. Suppose Point is a struct, and Form is a class. We can instantiate each as follows:

Point p1 = new Point();         // Point is a *struct*
Form f1 = new Form();           // Form is a *class*

In the first case, one space in memory is allocated for p1, wheras in the second case, two spaces are allocated: one for a Form object and another for its reference (f1). It's clearer when we go about it the long way:

Form f1;                        // Allocate the reference
f1 = new Form();                // Allocate the object

If we copy the objects to new variables:

Point p2 = p1;
Form f2 = f1;

p2, being a struct, becomes an independent copy of p1, with its own separate fields. But in the case of f2, all we’ve copied is a reference, with the result that both f1 and f2 point to the same object.

This is of particular interest when passing parameters to methods. In C#, parameters are (by default) passed by value, meaning that they are implicitly copied when passed to the method. For value-type parameters, this means physically copying the instance (in the same way p2 was copied), while for reference-types it means copying a reference (in the same way f2 was copied). Here is an example:

Point myPoint = new Point (0, 0);      // a new value-type variable
Form myForm = new Form();              // a new reference-type variable

Test (myPoint, myForm);                // Test is a method defined below

 

void Test (Point p, Form f)

{
      p.X = 100;                       // No effect on MyPoint since p is a copy
      f.Text = "Hello, World!";        // This will change myForm’s caption since
                                       // myForm and f point to the same object
      f = null;                        // No effect on myForm
}

Assigning null to f has no effect because f is a copy of a reference, and we’ve only erased the copy.

We can change the way parameters are marshalled with the ref modifier. When passing by “reference”, the method interacts directly with the caller’s arguments. In the example below, you can think of the parameters p and f being replaced by myPoint and myForm:

Point myPoint = new Point (0, 0);      // a new value-type variable
Form myForm = new Form();              // a new reference-type variable

Test (ref myPoint, ref myForm);        // pass myPoint and myForm by reference

 

void Test (ref Point p, ref Form f)

{
      p.X = 100;                       // This will change myPoint’s position
      f.Text = “Hello, World!”;        // This will change MyForm’s caption
      f = null;                        // This will nuke the myForm variable!
}

In this case, assigning null to f also makes myForm null, because this time we’re dealing with the original reference variable and not a copy of it.

Memory Allocation

The Common Language Runtime allocates memory for objects in two places: the stack and the heap. The stack is a simple first-in last-out memory structure, and is highly efficient.  When a method is invoked, the CLR bookmarks the top of the stack.  The method then pushes data onto the stack as it executes.  When the method completes, the CLR just resets the stack to its previous bookmark—“popping” all the method’s memory allocations is one simple operation!

In contrast, the heap can be pictured as a random jumble of objects.  Its advantage is that it allows objects to be allocated or deallocated in a random order.  As we’ll see later, the heap requires the overhead of a memory manager and garbage collector to keep things in order.

To illustrate how the stack and heap are used, consider the following method:

void CreateNewTextBox()
{
      TextBox myTextBox = new TextBox();             // TextBox is a class
}

In this method, we create a local variable that references an object. The local variable is stored on the stack, while the object itself is stored on the heap:

The stack is always used to store the following two things:

  • The reference portion of reference-typed local variables and parameters (such as the myTextBox reference)
  • Value-typed local variables and method parameters (structs, as well as integers, bools, chars, DateTimes, etc.)

The following data is stored on the heap:

  • The content of reference-type objects.
  • Anything structured inside a reference-type object.

Memory Disposal

Once CreateNewTextBox has finished running, its local stack-allocated variable, myTextBox, will disappear from scope and be “popped” off the stack. However, what will happen to the now-orphaned object on the heap to which it was pointing? The answer is that we can ignore it—the Common Language Runtime’s garbage collector will catch up with it some time later and automatically deallocate it from the heap.  The garbage collector will know to delete it, because the object has no valid referee (one whose chain of reference originates back to a stack-allocated object).[1] C++ programmers may be a bit uncomfortable with this and may want to delete the object anyway (just to be sure!) but in fact there is no way to delete the object explicitly. We have to rely on the CLR for memory disposal—and indeed, the whole .NET framework does just that!

However there is a caveat on automatic destruction. Objects that have allocated resources other than memory (in particular “handles”, such as Windows handles, file handles and SQL handles) need to be told explicitly to release those resources when the object is no longer required. This includes all Windows controls, since they all own Windows handles! You might ask, why not put the code to release those resources in the object’s finalizer?  (A finalizer is a method that the CLR runs just prior to an object’s destruction). The main reason is that the garbage collector is concerned with memory issues and not resource issues. So on a PC with a few gigabytes of free memory, the garbage collector may wait an hour or two before even getting out of bed!

So how do we get our textbox to release that Windows handle and disappear off the screen when we’re done with it? Well, first, our example was pretty artificial. In reality, we would have put the textbox control on a form in order to make it visible it in the first place. Assuming myForm was created earlier on, and is still in scope, this is what we’d typically do:

myForm.Controls.Add (myTextBox);

As well as making the control visible, this would also give it another referee (myForm.Controls). This means that when the local reference variable myTextBox drops out of scope, there’s no danger of the textbox becoming eligible for garbage collection. The other effect of adding it to the Controls collection is that the .NET framework will deterministically call a method called Dispose on all of its members the instant they’re no longer needed. And in this Dispose method, the control can release its Windows handle, as well as dropping the textbox off the screen.

All classes that implement IDisposable (including all Windows Forms controls) have a Dispose method. This method must be called when an object is no longer needed in order to release resources other than memory. There are two ways this happens:
 - manually (by calling Dispose explicitly)
 - automatically: by adding the object to a .NET container, such as a Form, Panel, TabPage or UserControl. The container will ensure that when it’s disposed, so are all of its members. Of course, the container itself must be disposed (or in turn, be part of another container).
In the case of Windows Forms controls, we nearly always add them to a container – and hence rely on automatic disposal.

The same thing applies to classes such as FileStream—these need to be disposed too. Fortunately, C# provides a shortcut for calling Dispose on such objects, in a robust fashion: the using statement:

using (Stream s = File.Create ("myfile.txt"))

{

   ...

}

This translates to the following code:

Stream s = File.Create ("myfile.txt");

try

{

   ...

}

finally

{

   if (s != null) s.Dispose();

}

The finally block ensurse that Dispose still gets executed should an exception be thrown within the main code block.

What about in WPF?

Most of the elements in WPF don’t wrap unmanaged handles requiring explicit disposal. So you can mostly ignore the disposal with WPF!

A Windows Forms Example

Let's look a couple more types you’ll come across often in Windows Forms applications. Size is a type used for representing a 2-dimensional extent and Font, as you would expect, encapsulates a font and its properties. You can find them in the .NET framework, in the System.Drawing namespace. The Size type is a struct—rather like Point, while the Font type is a class. We'll create an object of each type:

Size s = new Size (100, 100);          // struct = value type
Font f = new Font (“Arial”,10);        // class = reference type

and we’ll also create a form. Form is a class in System.Windows.Forms namespace, and is hence a reference type:

Form myForm = new Form();

To set the form's size and font, we can assign the objects s and f to the form via its properties:

myForm.Size = s;
myForm.Font = f;

 

Don't get confused by the double usage of the identifiers Size and Font: now they are referring to members of myForm and not the Size and Font classes. This double usage is acceptable in C# and is applied extensively throughout the .NET framework.

Here's what it now looks like in memory:

As you can see, with s, we've copied over its contents, while in the case of f, we've copied over its reference (resulting in two pointers in memory to the same Font object). This means that changes made via s will not affect the form, while changes made via f, will[2].

In-Line Allocation

Previously we said that for value-typed local variables, memory is allocated on the stack. So does that mean the newly copied Size struct is also allocated on the stack? The answer is no, because it’s not a local variable!  Instead, it’s stored in a field of another object (in this case a form) that’s allocated on the heap. Therefore, it must, too, be allocated on the heap. This mode of storage is called "in-line". 

Fun with Structs

We've made a slightly simplifying assumption in the diagrams in that Size and Font are depicted as fields in the Form class. More accurately, they are properties, which are facades for internal representations we don’t get to see. We can imagine their definitions look something like this:

class Form
{
      // Private field members

      Size size;
      Font font;

      // Public property definitions

      public Size Size
      {
            get    { return size; }
            set    { size = value; fire resizing events }
      }

      public Font Font
      {
            get    { return font; }
            set    { font = value; }
      }
}

By using properties, the class has an opportunity to fire events when the form’s size or font changes. It provides further flexibility in that other size-related properties, such as ClientSize (the size of a control’s internal area without title bar, borders, or scroll bars) can work in tandem with the same private fields.

But there is a snag. Suppose we want to double the form’s height, through one of its properties. It would seem reasonable to do this :

myForm.ClientSize.Height = myForm.ClientSize.Height * 2;

or more simply:

myForm.ClientSize.Height *= 2;

However, this generates a compiler error:

Cannot modify the return value of 'System.Windows.Forms.Form.ClientSize' because it is not a variable

We get the same problem whether we use Size or ClientSize. Let’s look at why.

Imagine ClientSize as a public field rather than a property. The expression myForm.ClientSize.Height would then simply reach through the membership hierarchy in a single step and access the Height member as expected. But since ClientSize is a property, myForm.ClientSize is first evaluated (using the property’s get method), returning an object of type Size. And because Size is a struct (and hence a value-type) what we get back is a copy of the form’s size. And it’s this copy whose size we double! C# realizes our mistake, and generates an error rather than compiling something that it knows won’t work.  (Had Size been defined instead as a class, there would have been no problem, since ClientSize’s get accessor would have returned a reference, giving us access to the form’s real Size object.)

So how then do we change the form’s size? You have to assign it a whole new object:

myForm.ClientSize = new Size
 (myForm.ClientSize.Width, myForm.ClientSize.Height * 2);

There’s more good news in that with most controls we usually size them via their external measurements (Size rather than ClientSize) and for these we also have ordinary integer Width and Height properties that we can get and set!

You might wonder if they could they have saved all this bother by defining Size as a class rather than a struct. But if Size was a class, its Height and Width properties would probably have been made read-only to avoid the complication of having to raise events whenever their values changed (so that the control can know to resize itself). And as read-only properties, you would be forced to go about changing them by creating a new object—so we’d be back to square one!

posted on 2011-03-17 22:36 luis 閱讀(601) 評(píng)論(0)  編輯 收藏 引用

只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


<2013年1月>
303112345
6789101112
13141516171819
20212223242526
272829303112
3456789

常用鏈接

留言簿(3)

隨筆分類(lèi)

隨筆檔案

文章分類(lèi)

文章檔案

友情鏈接

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美激情久久久久| 久久男女视频| 欧美精品激情在线| 国产精品丝袜久久久久久app| 亚洲成人自拍视频| 久久久久久色| 欧美在线地址| 国产精品劲爆视频| 一区二区三区欧美日韩| 欧美激情视频一区二区三区在线播放 | 久久免费精品视频| 国产伦精品一区二区三区视频黑人| 亚洲日本无吗高清不卡| 免费久久99精品国产| 久久成人免费网| 韩日成人av| 欧美成人免费观看| 另类尿喷潮videofree | 久久久久久久久伊人| 亚洲欧美国产日韩天堂区| 国产精品国产三级国产专播品爱网 | 性做久久久久久| 亚洲性xxxx| 国产一在线精品一区在线观看| 久久久久国产成人精品亚洲午夜| 欧美一区二区日韩一区二区| 国产一区二区三区日韩欧美| 美女露胸一区二区三区| 噜噜噜久久亚洲精品国产品小说| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲影院免费| 国产网站欧美日韩免费精品在线观看 | 国产精品欧美日韩一区| 欧美一区二区三区视频免费播放 | 欧美福利影院| 亚洲一区二区在线免费观看视频 | 国产女主播视频一区二区| 久久精品毛片| 免费成人高清视频| 亚洲免费一级电影| 欧美与黑人午夜性猛交久久久| 在线日韩电影| 一区二区三区欧美成人| 狠狠噜噜久久| 国产一级一区二区| 亚洲国产电影| 亚洲国产中文字幕在线观看| 欧美日韩国产不卡在线看| 午夜久久电影网| 久久免费视频在线| 亚洲网站视频福利| 欧美中文在线免费| 99视频精品全部免费在线| 亚洲欧美在线播放| 亚洲国产成人在线视频| 亚洲视频www| 亚洲国产视频a| 亚洲已满18点击进入久久| 亚洲人线精品午夜| 性做久久久久久久免费看| 日韩亚洲视频| 久久福利一区| 亚洲一区二区三区高清| 久久午夜精品一区二区| 欧美一级午夜免费电影| 欧美精品日韩一区| 美女视频一区免费观看| 国产精品一区在线播放| 亚洲乱码日产精品bd| 亚洲第一毛片| 久久大香伊蕉在人线观看热2| 亚洲视频综合| 欧美激情在线观看| 国产日韩综合| 午夜老司机精品| 欧美一区二粉嫩精品国产一线天| 欧美国产日韩二区| 欧美一级夜夜爽| 国产精品视频xxx| 一区二区动漫| 国产精品久久久久一区| 亚洲精品一区二区在线| 一本色道久久综合亚洲精品小说| 国产精品理论片| 亚洲激情国产精品| 久久久久久久久久久久久9999| 久久大逼视频| 国产偷国产偷亚洲高清97cao| 亚洲一区在线播放| 老司机亚洲精品| 在线性视频日韩欧美| 亚洲女同精品视频| 亚洲国产另类久久久精品极度| 亚洲最新视频在线| 另类av一区二区| 欧美电影免费网站| 亚洲欧洲日本专区| 欧美成人精品一区二区| 亚洲国产日韩美| 日韩一级裸体免费视频| 欧美日韩大片| 亚洲视频在线观看免费| 午夜免费电影一区在线观看| 国产农村妇女精品一区二区| 国产精品白丝黑袜喷水久久久| 亚洲欧美日韩成人| 亚洲综合三区| 国产精品久久国产精麻豆99网站| 日韩一二三区视频| 亚洲在线网站| 国产一区二区精品久久91| 欧美一区在线看| 免费亚洲网站| 日韩视频在线一区二区| 欧美视频一区二区三区四区| 亚洲午夜视频| 久久综合久久综合九色| 亚洲区国产区| 国产精品成人观看视频国产奇米| 亚洲一区bb| 老鸭窝毛片一区二区三区| 亚洲人体影院| 国产亚洲成年网址在线观看| 久久精品在线| 亚洲国产经典视频| 午夜精品福利一区二区三区av| 国产综合在线看| 欧美华人在线视频| 亚洲欧美一区二区三区在线| 欧美 日韩 国产一区二区在线视频| 亚洲美女诱惑| 国产香蕉97碰碰久久人人| 欧美成黄导航| 亚洲欧美精品在线观看| 亚洲成人在线视频网站| 久久精品国产亚洲aⅴ| 欧美激情亚洲激情| 欧美一区二区黄色| 日韩视频二区| 在线观看久久av| 国产精品美女午夜av| 久久久水蜜桃av免费网站| aa国产精品| 亚洲国产另类久久精品| 久久久久久亚洲精品中文字幕| 亚洲美女视频| 在线免费日韩片| 国产欧美一区二区三区国产幕精品| 欧美国产日韩精品免费观看| 欧美一区二区三区男人的天堂| 亚洲国产精品一区二区第一页| 久久国产精彩视频| 亚洲一区二区精品| 亚洲裸体俱乐部裸体舞表演av| 国内外成人免费激情在线视频| 国产精品国产一区二区| 欧美日韩福利| 欧美激情综合网| 榴莲视频成人在线观看| 国内揄拍国内精品久久| 国产精品久久久久影院亚瑟 | 欧美视频国产精品| 欧美国产精品| 久久久久久久久久久久久久一区 | 久久久久久9| 午夜精品久久久| 亚洲午夜女主播在线直播| 99国产精品久久久| 亚洲激情校园春色| 欧美 日韩 国产精品免费观看| 久久久久综合网| 久久疯狂做爰流白浆xx| 羞羞漫画18久久大片| 亚洲一区在线直播| 久久精品女人| 亚洲乱码国产乱码精品精天堂| 国产日韩欧美电影在线观看| 欧美日韩在线播放三区| 欧美—级在线免费片| 欧美高清视频在线| 欧美成人精品福利| 欧美精品一区三区| 欧美乱妇高清无乱码| 欧美日韩hd| 欧美日韩一区免费| 国产精品av久久久久久麻豆网| 欧美日韩国产区| 欧美三日本三级少妇三99| 欧美午夜在线一二页| 国产精品久久久久91| 国产欧美精品一区二区色综合| 国产欧美日韩视频一区二区三区| 国产精品无码永久免费888| 国产精品一区二区三区四区| 国产欧美丝祙| 亚洲国产精品一区二区第四页av | 欧美高清视频一二三区| 亚洲二区三区四区| 日韩亚洲国产精品| 亚洲免费在线视频|