C# 值类型和引用类型 Value Type and Reference Type

根据它们在内存中存储值的方式进行分类。 C# 数据类型有下面的三种分类:

  • 值类型
  • 引用类型
  • 指针类型

栈和堆 stack and heap.

当我们的代码执行时,内存中有两个地方用来存储这些代码。分别是栈和堆。栈和堆都用来帮助我们运行代码的,它们驻留在机器内存中,且包含所有代码执行所需要的信息。

  1. 栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义;堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小 存放在栈中时要管存储顺序,保持着先进后出的原则,他是一片连续的内存域,有系统自动分配和维护。

  2. 而堆是无序的,他是一片不连续的内存域,有用户自己来控制和释放,如果用户自己不释放的话,当内存达到一定的特定值时,通过垃圾回收器(GC)来回收。

值类型 Value Type

值类型的数据是放在栈当中的。(因为它在定义的时候就可以知道它的具体大小了),(也叫放在自己声明的位置,要结合程序是如何运行的来理解)

例如,考虑整数变量 int i = 100;

系统在为变量 i 分配的内存空间中存储 100。 下图说明了 100 是如何存储在内存中的某个假设位置 (0x248120) 的“i”的:

值类型都是继承于System.ValueType (System.ValueType也是继承于System.Object) 下面的这些都是值类型

  • bool
  • byte
  • char
  • decimal
  • double
  • enum
  • float
  • int
  • long
  • sbyte
  • short
  • struct
  • uint
  • ulong
  • ushort
  • DateTime

值类型 参数值传递的特点

  1. 当您将值类型变量从一种方法传递到另一种方法时,系统会在另一种方法中创建变量的单独副本。 如果在一个方法中更改了值,则不会影响另一种方法中的变量。
  2. 同一个方法里面把一个变量赋给另一个变量,系统也是创建这个变量的副本。 例 1
   static void ChangeValue(int x)
        {
            x = 200;

            Console.WriteLine(x); //200
        }

        static void Main(string[] args)
        {
            int i = 100;

            Console.WriteLine(i); //100

            ChangeValue(i);

            Console.WriteLine(i); // 100
        }

例 2

static void Main(string[] args)
{
	int i = 100;
	int b = i;
	Console.WriteLine(b); // 100
	b = 300;
	Console.WriteLine(b); // 300
	Console.WriteLine(i); // 100
}

引用类型 Reference Type

与值类型不同,引用类型不直接存储其值。 相反,它存储存储值的地址。 换句话说,引用类型包含一个指向保存数据的另一个内存位置的指针(放在堆中)。

例如,考虑以下字符串变量: string s = "Hello World!!"; 下图显示了系统如何为上述字符串变量分配内存。

如上图所示,系统为变量 s 在栈中选择一个随机位置 (0x248100)。 一个变量s的值为0x700000,即实际数据值的内存地址。 因此,引用类型存储实际值存储位置的地址,而不是值本身。

下面的这些都是引用类型

  • String
  • Arrays (不管它的元素是不是值类型)
  • Class
  • Delegate

引用类型 传递的特点

当您将引用类型变量从一种方法传递到另一种方法时,它不会创建新副本; 相反,它传递变量的地址。 所以,如果我们在一个方法中改变了一个变量的值,它也会反映在调用方法中。 (在同一个方法中也是一样的)。

class Program
{
    static void ChangeReferenceType(Student std2)
    {
        std2.Age = 200;
    }

    static void Main(string[] args)
    {
        Student std1 = new Student();
        std1.Age = 100;
        ChangeReferenceType(std1);
        Console.WriteLine(std1.Age);//200
        var std2 = std1; //改的是std2的值,但是std1的值也变了。
        std2.Age = 300;
        Console.WriteLine(std1.Age);//300
    }
}

public class Student
{
    public int Age { get; set; }
}

特例 String 是一种引用类型,但它是不可变的。 这意味着一旦我们分配了一个值,它就无法更改。 如果我们更改一个字符串值,那么编译器会在内存中创建一个新的字符串对象,并将一个变量指向新的内存位置。 因此,将字符串值传递给函数会在内存中创建一个新变量,函数中值的任何更改都不会反映在原始值中,如下所示。 (表现形式跟值类型一样了)

        static void ChangeReferenceType(string name)
        {
            name = "Steve";
        }

        static void Main(string[] args)
        {
            string name = "malema.net";

            ChangeReferenceType(name);

            Console.WriteLine(name); //malema.net
        }

null

引用类型变量未初始化时的默认值为 null。 Null 表示不引用任何对象。 (以后程序会经常的碰到未将对象引用设置到对象实例,就是这个对象变成了null) 值类型变量不能为null,因为它保存的是值,而不是内存地址。 C# 2.0 引入了可空类型,您可以使用它为值类型变量分配 null 或声明值类型变量而不为其分配值。

上一篇:C# 可空类型
下一篇:C# 接口
最近更新的
...