2020.07.01 C# 值类型/引用类型

值类型/引用类型

- 值类型:

值类型的变量包含类型的实例。 它不同于引用类型的变量,后者包含对类型实例的引用。 默认情况下,在分配中,通过将实参传递给方法并返回方法结果来复制
变量值。 对于值类型变量,会复制相应的类型实例。

using System; 
public struct MutablePoint 
{
	public int X; 
	public int Y;
	public MutablePoint(int x, int y) => (X, Y) = (x, y); 
	public override string ToString() => $"({X}, {Y})";
}
public class Program 
{
	public static void Main() 
	{
		var p1 = new MutablePoint(1, 2); 
		var p2 = p1; p2.Y = 200; 
		Console.WriteLine($"{nameof(p1)} after {nameof(p2)} is modified: {p1}"); 
		Console.WriteLine($"{nameof(p2)}: {p2}");
		MutateAndDisplay(p2); 
		Console.WriteLine($"{nameof(p2)} after passing to a method: {p2}");
	}
	private static void MutateAndDisplay(MutablePoint p) 
	{
		p.X = 100; Console.WriteLine($"Point mutated in a method: {p}");
	}
} 
// Expected output: 
// p1 after p2 is modified: (1, 2) 
// p2: (1, 200) 
// Point mutated in a method: (100, 200)
// p2 after passing to a method: (1, 200)

如前面的示例所示,对值类型变量的操作只影响存储在变量中的值类型实例。 如果值类型包含引用类型的数据成员,则在复制值类型实例时,只会复制对引用类型实例的引用。 副本和原始值类型实例都具有对同一引用类型实例的访问权限。 以下示例演示了该行为:

using System; 
using System.Collections.Generic;
public struct TaggedInteger 
{
	public int Number; private List<string> tags;
	public TaggedInteger(int n) 
	{
		Number = n; tags = new List<string>();
	} 
	public void AddTag(string tag) => tags.Add(tag); 
	public override string ToString() => $"{Number} [{string.Join(", 		", tags)}]";
}
public class Program 
{
	public static void Main() 
	{
		var n1 = new TaggedInteger(0);
		n1.AddTag("A"); 
		Console.WriteLine(n1); 
		// output: 0 [A]
		var n2 = n1; 
		n2.Number = 7; 
		n2.AddTag("B");
		Console.WriteLine(n1); 
		// output: 0 [A, B] Console.WriteLine(n2);
		// output: 7 [A, B]
	}
}

值类型的分类:

  1. 结构类型,用于封装数据和相关功能
  2. 枚举类型,由一组命名常数定义,表示一个选择或选择组合

注意:不能将null直接分配给值类型的变量,除非他是可空的值类型T?

C#内置的值类型 “简单类型”:

整数型值类型:

可以 使用文本进行初始化。 所有整型数值类型都支持算术、位逻辑、比较和相等运算符。

C#整型类型.Net 整形类型意义范围
sbyteSystem.SByte8位带符号整数-128 到 127
byteSystem.Byte无符号的8位整数0到255
shortSystem.Int1616位带符号整数-32,768 到 32,767
ushortSystem.UInt16无符号的16位整数0到65535
intSystem.Int3232位带符号整数-2,147,483,648 到 2,147,483,647
uintSystem.UInt32无符号的32位整数0到4,294,967,295
longSystem.Int6464位带符号整数
ulongSystem.UInt64无符号的64位整数

浮点数值类型:

可以使用文本进行初始 化。 所有浮点数值类型都支持算术、比较和相等运算符。

C#整型类型.Net 整形类型大小范围
floatSystem.Single4个字节 大约,6-9 位数字±1.5 x 10−45 至 ±3.4 x 1038
doubleSystem.Double8个字节,大约 15-17 位数字±5.0 x 10−324 至 ±1.7 x 10308
decimalSystem.Decimal16个字节,28-29 位±1.0 x 10−28 至 ±7.9228 x 1028
  • floatdouble 相比, decimal 类型具有更高的精度和更小的范围,因此它适合于财务和货币计算。

bool 布尔值:
bool 类型关键字是 .NET System.Boolean 结构类型的别名,它表示一个布尔值,可为 truefalse 。 若要使用 bool 类型的值执行逻辑运算,请使用布尔逻辑运算符。 bool 类型是比较和相等运算符的结果类型。 bool 表达式可以是 ifdowhilefor 语句中以及条件运算符 ?: 中的控制条件表达式。
bool 类型的默认值为 false

char Unicode UTF-16字符:

  • char 类型关键字是 .NET System.Char 结构类型的别名,它表示 Unicode UTF-16 字符。
  • char 类型的默认值为 \0 ,即 U+0000。 char 类型支持比较、相等、增量和减量运算符。
  • 此外,对于 char 操作数,算数和逻辑位运算符对相应的字符代码执行操作,并得出 int 类型的结果。
  • 字符串类型将文本表示为 char 值的序列。
C#整型类型.Net 整形类型大小范围
charSystem.Char16 位U+0000 到 U+FFFF

注意:

  • 所有简单值都是结构类型,它们与其他结构类型的不同之处在于,它们允许特定的额外操作: 可以使用文字为简单类型提供值。 例如, ‘A’ 是类型char 的文本, 2001 是类型 int 的文本。
  • 可以使用 const 关键字声明简单类型的常数。 不能具有其他结构类型的常数。
  • 常数表达式的操作数都是简单类型的常数,在编译时进行评估。
  • 从 C# 7.0 开始,C# 支持值元组。 值元组是值类型,而不是简单类型。

枚举类型:
定义枚举类型,请使用 enum 关键字并指定枚举成员的名称:

enum Season
{ 
	Spring, 
	Summer, 
	Autumn, 
	Winter
}

结构类型:
结构类型(“structure type”或“struct type”)是一种可封装数据和相关功能的值类型。使用 struct 关键字定义结构类型:

public struct Coords 
{
	public Coords(double x, double y) 
	{
	X = x; 
	Y = y;
	}
	public double X { get; } public double Y { get; } 	
	public override string ToString() => $"({X}, {Y})";
}
  • 不能声明无参数构造函数。 每个结构类型都已经提供了一个隐式无参数构造函数,该构 造函数生成类型的默认值。
  • 不能在声明实例字段或属性时对它们进行初始化。 但是,可以在其声明中初始化静 态或常量字段或静态属性。
  • 结构类型的构造函数必须初始化该类型的所有实例字段。
  • 结构类型不能从其他类或结构类型继承,也不能作为类的基础类型。 但是,结构类型可 以实现接口。
  • 不能在结构类型中声明终结器。

C# 中,必须先初始化已声明的变量,然后才能使用该变量。 由于结构类型变量不能为 null (除非它是可为空的值类型的变量),因此,必须实例化相应类型的实例。 有多种方法可 实现此目的。通常,可使用 new 运算符调用适当的构造函数来实例化结构类型。 每个结构类型都至少有一 个构造函数。 这是一个隐式无参数构造函数,用于生成类型的默认值。 还可以使用默认值表达 式来生成类型的默认值。如果结构类型的所有实例字段都是可访问的,则还可以在不使用 new 运算符的情况下对其进 行实例化。 在这种情况下,在首次使用实例之前必须初始化所有实例字段。 下面的示例演示如何执行此操作:

public static class StructWithoutNew 
{
	public struct Coords 
	{
	public double x; 
	public double y;
	}
	public static void Main() 
	{
		Coords p; 
		p.x = 3; 
		p.y = 4; 
		Console.WriteLine($"({p.x}, {p.y})"); // output: (3, 4)
	}
}
- 引用类型:

引用类型的变量存储对其数据(对象)的引用,而值类型的变量直接包含其数据。 对于引用类型,两种变量可引用同一对象;因此,对一个变量执行的操作会影响另一个变量所引用的对象。 对于值类型,每个变量都具有其自己的数据副本,对一个变量执行的操作不会影响另一个变量(in、ref 和 out 参数变量除外)。

用于声明引用类型的关键字:classdelegateinterface

C#内置的引用类型有:dynamicobjectstring

内置引用类型:
对象类型:
object 类型是 System.Object 在 .NET 中的别名。 在 C# 的统一类型系统中,所有类型(预定义 类型、用户定义类型、引用类型和值类型)都是直接或间接从System.Object 继承的。 可以将任 何类型的值赋给 object 类型的变量。 可以使用文本 null 将任何 object 变量赋值给其默认 值。 将值类型的变量转换为对象的过程称为装箱。 将 object 类型的变量转换为值类型的过程称为取消装箱 。

字符串类型:
尽管 string 为引用类型,但是定义相等运算符 == 和 != 是为了比较 string 对象(而不是引 用)的值。 这使得对字符串相等性的测试更为直观。

委托类型:
委托类型的声明与方法签名相似。 它有一个返回值和任意数目任意类型的参数:

public delegate void MessageDelegate(string message); 
public delegate int AnotherDelegate(MyType m, long num);

在 .NET 中, System.Action 和 System.Func 类型为许多常见委托提供泛型定义。 可能不需要定 义新的自定义委托类型。 相反,可以创建提供的泛型类型的实例化。delegate 是一种可用于封装命名方法或匿名方法的引用类型。

动态类型:
dynamic 类型表示变量的使用和对其成员的引用绕过编译时类型检查。 改为在运行时解析这些 操作。 dynamic 类型简化了对 COM API(例如 Office Automation API)、动态 API(例如 IronPython 库)和 HTML 文档对象模型 (DOM) 的访问。
在大多数情况下, dynamic 类型与 object 类型的行为类似。 具体而言,任何非 Null 表达式都 可以转换为 dynamic 类型。 dynamic 类型与 object 的不同之处在于,编译器不会对包含类型 dynamic 的表达式的操作进行解析或类型检查。 编译器将有关该操作信息打包在一起,之后这
些信息会用于在运行时评估操作。 在此过程中, dynamic 类型的变量会编译为 object 类型的
变量。 因此, dynamic 类型只在编译时存在,在运行时则不存在。

Class:
C# 中仅允许单一继承。 也就是说,一个类仅能从一个基类继承实现。 但是,一个类可实现 多个接口。 可以声明具有类型参数的泛型类。
一个类可包含下列成员的声明:

构造函数 常量 字段 终结器 方法 属性 索引器
运算符事件 委托 类 接口 结构类型

Interface:
接口定义协定。 实现该协定的任何 class 或 struct 必须提供接口中定义的成员的实现。 从 C# 8.0 开 始,接口可为成员定义默认实现。 它还可以定义 static 成员,以便提供常见功能的单个实现。接口可以是命名空间或类的成员。 接口声明可以包含以下成员的声明方法 属性 索引器 事件

Delegate:
如上

可为空的引用类型:
引用类型 T 的变量必须用非 null 值进行初始化,并且不能为其分配可能为 null 的值。
引用类型 T? 的变量可以用 null 进行初始化,也可以分配 null ,但在取消引用之前必须对照 null 进行检查。
类型为 T? 的变量 m 在应用 null 包容运算符时被认为是非空的,如 m! 中所示。
变量 notNull 和 nullable 都由 String 类型表示。 因为不可为 null 的类型和可为 null 的类型都存储为相同的类 型,所以有几个位置不允许使用可为 null 的引用类型。 通常,可为 null 的引用类型不能用作基类或实现的接口。 可为 null 的引用类型不能用于任何对象创建或类型测试表达式。 可为 null 的引用类型不能是成员访问表达式的
类型。

参考:微软C#官方手册

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_41906368

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值