C# 中的值和引用类型赋值
介绍
就编程语言的类型系统而言,主要有两类:强类型编程语言和弱类型编程语言。这些类别定义了在源代码编译和运行时如何应用类型规则。1974 年,Barbara Liskov 和 Stephen Zilles 这样定义了强类型编程语言。
每当一个对象从调用函数传递到被调用函数时,其类型必须与被调用函数中声明的类型兼容。
在本指南中,我们将探讨为什么 C# 属于强类型语言类别以及这如何影响类型分配,特别是涉及值和引用类型。
类型和上下文
在 .NET 框架中,以及 C# 编程语言中,有两种主要类型:值类型和引用类型。C# 中也有两种上下文,分别是安全和不安全。需要指出的是,在不安全上下文中,我们有第三种类型,称为指针。现在,只要知道当代码在 CLR 的监督下运行时,我们称上下文安全就足够了,而不安全的代码则恰恰相反。
价值与参考
有四种类型属于引用类型类别:
- 细绳
- 数组
- 班级
- 代表
其余类型属于值类型类别:
- 布尔值
- 字节
- 炭
- 十进制
- ETC。
值类型
让我们看一下值类型的示例。在此代码中,我们有一个函数,它接受double类型的值并对其进行平方。
using System;
namespace Pluralsight
{
public class ValueType
{
public static void SquareIt(double x)
{
Console.WriteLine($"The value of x: {x} squared is: {x * x}");
}
public static void Main()
{
double d = 22.22;
SquareIt(d);
Console.ReadKey();
}
}
}
如果我们将22.22作为函数的输入,则输出如下所示。
The value of x: 22.22 squared is: 493.7284
幕后发生了什么?d的内容的值被分配在堆栈上。根据类型在内存中保留一个单独的空间,并且此变量直接保存该值。如果我们通过赋值将此值复制到另一个变量,则该值将被复制,因此您将在堆栈上保留两次d的值,每个变量保留一次。每个预定义数据类型(包括枚举甚至结构)都以这种方式工作。值类型是在编译时创建的,因此,它们受到保护,不受垃圾收集器的访问 - 垃圾收集器无法访问它们。
引用类型
我们也来看一个这样的演示,然后对其进行分析。
using System;
namespace Pluralsight
{
public class ReferenceType
{
public class Server
{
public string type;
public int vCPU;
public double RAM;
}
public static void initializeServer(string category, Server s)
{
if(category == "small")
{
s.type = "smallServer";
s.vCPU = 1;
s.RAM = 2.0;
}
else if(category == "medium")
{
s.type = "mediumServer";
s.vCPU = 2;
s.RAM = 4.0;
}
else if(category == "big")
{
s.type = "bigServer";
s.vCPU = 8;
s.RAM = 16.0;
} else { throw new System.ArgumentException("Category cannot be other than: small, medium or big"); }
}
public static void Main()
{
Server a = new Server();
Server b = new Server();
Server c = new Server();
initializeServer("small", a);
initializeServer("medium", b);
initializeServer("big", c);
Console.WriteLine($"The server:{nameof(a)}, is of type: {a.type}, vCPU: {a.vCPU} and RAM: {a.RAM} GB!");
Console.WriteLine($"The server:{nameof(b)}, is of type: {b.type}, vCPU: {b.vCPU} and RAM: {b.RAM} GB!");
Console.WriteLine($"The server:{nameof(c)}, is of type: {c.type}, vCPU: {c.vCPU} and RAM: {c.RAM} GB!");
Console.ReadKey();
}
}
}
输出如下所示:
The server:a, is of type: smallServer, vCPU: 1 and RAM: 2 GB!
The server:b, is of type: mediumServer, vCPU: 2 and RAM: 4 GB!
The server:c, is of type: bigServer, vCPU: 8 and RAM: 16 GB!
我们有一个引用类型的类。这一点很重要,因为初始化它的函数(名为initializeServer)需要两个参数。第一个是值类型,第二个是引用类型,它是类传递的位置。该函数将根据我们传递的类别更改通过引用传递的类的公共属性。如果我们传递未实现的类别,则会抛出错误并显示以下消息:
System.ArgumentException: 'Category cannot be other than: small, medium or big'
幕后发生了什么?服务器类实例 a、b 和 c 是引用类型。这意味着当我们将它们传递给initialiseServer函数时,传递的是它们的地址,而不是带有数据的实例。当我们将引用变量分配给另一个变量时,我们不会复制数据。我们只是告诉新变量也引用原始地址。这意味着如果我们要修改其中任何一个,则更改将反映在两者上,因为它们指向内存中的同一位置。引用类型变量存储在内存中称为堆的指定空间中。因此,当我们的应用程序中不再使用这样的变量时,它将被垃圾收集器释放。
关于堆栈与堆的说明:堆栈用于静态内存分配,而堆用于动态内存分配。它们都很有用,但用途不同。
结论
在本指南中,我们研究了值类型和引用类型。通过几个示例,我们剖析了这些类型的用途以及它们在编程上有何不同。我们还了解到 C# 有两种主要的内存类型。我希望本指南对您有所帮助,并感谢您阅读它。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~