zoukankan      html  css  js  c++  java
  • C# 值类型与引用类型 (上)

    1. 主要内容

                 类型的基本概念

                 值类型深入

                 引用类型深入

                 值类型与引用类型的比较及应用

    2. 基本概念

    C#中,变量是值还是引用仅取决于其数据类型。

    C#的基本数据类型都以平台无关的方式来定义,C#的预定义类型并没有内置于语言中,而是内置于.NET Framework中。.NET使用通用类型系统(CTS)定义了可以在中间语言(IL)中使用的预定义数据类型,所有面向.NET的语言都最终被编译为 IL,即编译为基于CTS类型的代码,

    通用类型的系统的功能:

    • 建立一个支持跨语言集成、类型安全和高性能代码执行的框架。
    • 提供一个支持完整实现多种编程语言的面向对象的模型。
    • 定义各语言必须遵守的规则,有助于确保用不同语言编写的对象能够交互作用。

        CTS_01           clrcts

    例如,在C#中声明一个int变量时,声明的实际上是CTS中System.Int32的一个实例。这具有重要的意义:

    • 确保IL上的强制类型安全;
    • 实现了不同.NET语言的互操作性;
    • 所有的数据类型都是对象。它们可以有方法,属性,等。例如:

    int i; 
    i = 1; 
    string s; 
    s = i.ToString();

    CLR 支持两种类型:值类型引用类型,

    C#的所有值类型均隐式派生自System.ValueType:

    • 结构体:struct(直接派生于System.ValueType);
      • 数值类型:
        • 整 型:sbyte(System.SByte的别名),short(System.Int16),int(System.Int32),long (System.Int64),byte(System.Byte),ushort(System.UInt16),uint (System.UInt32),ulong(System.UInt64),char(System.Char);
        • 浮点型:float(System.Single),double(System.Double);
        • 用于财务计算的高精度decimal型:decimal(System.Decimal)。
      • bool型:bool(System.Boolean的别名);
      • 用户定义的结构体(派生于System.ValueType)。
    • 枚举:enum(派生于System.Enum);
    • 可空类型(派生于System.Nullable<T>泛型结构体,T?实际上是System.Nullable<T>的别名)。

    值类型(Value Type),值类型实例通常分配在线程的堆栈(stack)上,并且不包含任何指向实例数据的指针,因为变量本身就包含了其实例数据

    C#有以下一些引用类型:

    • 数组(派生于System.Array)
    • 用户用定义的以下类型:
      • 类:class(派生于System.Object);
      • 接口:interface(接口不是一个“东西”,所以不存在派生于何处的问题。Anders在《C# Programming Language》中说,接口只是表示一种约定[contract]);
      • 委托:delegate(派生于System.Delegate)。
    • object(System.Object的别名);
    • 字符串:string(System.String的别名)。

    可以看出:

    • 引用类型与值类型相同的是,结构体也可以实现接口;
    • 引用类型可以派生出新的类型,而值类型不能;
    • 引用类型可以包含null值,值类型不能(可空类型功能允许将 null 赋给值类型);
    • 引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值

    2.1内存深入

    2.2.1 内存机制

    数据在内存中分配位置取决与该变量的数据类型,上图可知值类型分配在线程的堆栈上,引用类型则分配在托管堆上,由GC控制回收,以下代码和图演示了引用类型和值类型的区别:

    private static class ReferenceVsValue { 
          // Reference type (because of 'class') 
          private class SomeRef { public Int32 x; }

          // Value type (because of 'struct') 
          private struct SomeVal { public Int32 x; }

          public static void Go() { 
             SomeRef r1 = new SomeRef();   //在堆上分配

             SomeVal v1 = new SomeVal();   // 在栈上分配 
             r1.x = 5;                     // 提领指针

             v1.x = 5;                     // 在栈修改 
             Console.WriteLine(r1.x);      // 显示”5”

             Console.WriteLine(v1.x);      //同样显示”5”  
             // 下图左半部分反映了执行以上代码之后的情形

             SomeRef r2 = r1;              //只复制引用(指针) 
             SomeVal v2 = v1;              // 在栈上分配并且复制成员 
             r1.x = 8;                     // r1.x和r2.x都会更改

             v1.x = 9;                     // 只是更改v1.x,不会更改v2.x 
             Console.WriteLine(r1.x);      // 显示 "8" 
             Console.WriteLine(r2.x);      // 显示 "8" 
             Console.WriteLine(v1.x);      // 显示 "9" 
             Console.WriteLine(v2.x);      // 显示 "5"  
             //右半部分反映了在执行所有代码之后的情况 
          } 
       } 
                                       图5-1       图解代码执行时的内存分配情况

     GC01

    SomeVal是用Struct来声明的,而不是用常用的Class,在C#中用Struct声明的是值类型,每个变量或者程序都有自己的堆栈,不同的变量不能公用一个内存地址因此上图中SomeRef和SomeVal一定占用了不同的堆栈,变量经过传递后,对v1变量改变时,显然不会影响到v2的数据,可以看出,堆栈中的v1,v2包含其实际数据,而r1,r2则在堆栈中保存了其实例数据的引用地址,实际的数据保存在托管堆中,因此就有可能不同变量保存了 同一地址的数据引用,当从一个引用类型变量传递到另外一个相同的引用类型变量时,传递的是引用地址而不是实际的数据,所以改变一个变量的值会影响到另外一个变量的值,值类型与引用类型在内存中的分配是决定其应用不同的根本原因,由此可以容易的解释为什么传递参数的时候,按值传递不会改变形参的值,而按地址传递会改变形参的值。

    内存分配的几点:

    • 值类型变量做为局部变量时,该实例将被创建在堆栈上;而如果值类型变量作为类型的成员变量时,它将作为类型实例数据的一部分,同该类型的其他字段都保存在托管堆上,将在接下来的嵌套结构部分来详细说明问题。

    • 引用类型变量数据保存在托管堆上,但是根据实例的大小有所区别,如下:如果实例的大小小于85000Byte时,则该实例将创建在GC堆上;而当实例大小大于等于85000byte时,则该实例创建在LOH(Large Object Heap)堆上。

    2.2.2嵌套类型

    嵌套结构就是在值类型中嵌套定义了引用类型,或者在引用类型变量中嵌套定义了值类型

      • 引用类型嵌套值类型

    public class NestedValueinRef 

    //aInt做为引用类型的一部分将分配在托管堆上 
    private int aInt; 
    public NestedValueinRef 

    //aChar则分配在该段代码的线程栈上 
    char achar = 'a'; 

    }                                      图5-2 内存分配图可以表示为:

          GC003

    • 值类型嵌套引用类型

                引用类型嵌套在值类型时,内存的分配情况为:该引用类型将作为值类型的成员变量,堆栈上将保存该成员的引用,而成员的实际数据还是保存在托管堆中.

                  public struct NestedRefinValue 
                      { 
                              public MyClass myClass; 
                              public NestedRefinValue 
                          { 
                                    myClass.X = 1; 
                                    myClass.Y = 2; 
                          } 
                      }

                                        图5-3 内存分配图可以表示为:

          GC05

  • 相关阅读:
    web服务器-Apache
    nginx优化
    nginx下载限速
    nginx-URL重写
    HDU 5358 First One 求和(序列求和,优化)
    HDU 5360 Hiking 登山 (优先队列,排序)
    HDU 5353 Average 糖果分配(模拟,图)
    UVALive 4128 Steam Roller 蒸汽式压路机(最短路,变形) WA中。。。。。
    HDU 5348 MZL's endless loop 给边定向(欧拉回路,最大流)
    HDU 5344 MZL's xor (水题)
  • 原文地址:https://www.cnblogs.com/zxtceq/p/5219834.html
Copyright © 2011-2022 走看看