C#的值类型和引用类型
值类型
定义
值类型(value types
)是存储在栈上(stack
)而不是堆上(heap
)的数据类型。值类型直接包含其数据,赋值操作会复制数据本
常见的值类型
1.基本数据类型
int
:32位整数。long
:64位整数。short
:16位整数。byte
:8位无符号整数。uint
:32位无符号整数。ulong
:64位无符号整数。ushort
:16位无符号整数.sbyte
:8位有符号整数。float
:32位浮点数。double
:64位浮点数.decimal
:128位高精度小数。
2.字符类型
char
:16位 Unicode 字符。
3.布尔类型
bool
:表示真 (true
) 或假 (false
)。
4.结构体类型
例如 DateTime
、TimeSpan
、Point
、Complex
等。这些是用户定义的值类型。
定义
结构体类型(struct
)是C#中的一种数据类型,用于定义一个值类型的数据结构。与类(class
)不同,结构体是值类型,意味着它们存储数据的实际值,而不是数据的引用。结构体在内存中通常分配在栈上,具有轻量级和高效的特点。结构体的主要用途是表示简单的数据结构,如点、矩形、时间等。它们不支持继承,但可以实现接口,并且无法定义显式的无参构造函数。
示例
值类型的变量直接包含数据,而不是数据的引用。赋值操作会将数据的副本传递给新的变量。
struct Point
{
public int X;
public int Y;
}
Point p1 = new Point { X = 10, Y = 20 };
Point p2 = p1; // 这里p2是p1的副本
p2.X = 30;
Console.WriteLine(p1.X); // 输出 10,p1的值没有改变
5.枚举类型
定义
例如 DayOfWeek
、ConsoleColor
等。枚举类型是由用户定义的一组命名的常数
枚举的特点
1.默认值:枚举中的第一个成员的默认值为0
,后续成员的值依次递增。你可以显式指定每个成员的值。
2.基础类型:默认为int
,但你可以指定其他整型类型(如byte
、short
、long
等)作为基础类型。
enum DayOfWeek : byte {
Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
}
3.使用枚举:枚举类型可以用作变量类型,赋值给枚举变量,进行比较和操作
DayOfWeek today = DayOfWeek.Monday;
//为today定义一个初始值
if (today == DayOfWeek.Monday)
{
Console.WriteLine("Today is Monday.");
}
4.可以将枚举值转换为其基础类型的值,或将整型值转换为枚举类型。
int dayValue = (int)DayOfWeek.Wednesday; // dayValue = 3
DayOfWeek day = (DayOfWeek)3; // day = DayOfWeek.Wednesday
5.使用Enum
类的方法
Enum
类提供了一些方法,用于操作和查询枚举值,例如Enum.GetValues
、Enum.GetNames
等
foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek)))
{
//Enum.GetValues 是一个静态方法,用于获取指定枚举类型的所有值
Console.WriteLine(day);
}
引用类型
1.class
2.delegate(用于声明委托)
3.interface(接口)
定义
接口可以包含方法、属性、事件和索引器的声明,但不能包含字段、构造函数或实现这些成员的代码。它们的主要作用是定义类或结构体必须实现的一组成员。
使用接口的基本示例
定义一个接口
public interface IShape
{
double Area(); // 方法声明
double Perimeter { get; } // 属性声明
}
实现接口的类
public class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
// 实现接口的方法
public double Area()
{
return Width * Height;
}
// 实现接口的属性
public double Perimeter
{
get { return 2 * (Width + Height); }
}
}
使用接口
IShape shape = new Rectangle { Width = 5, Height = 10 };
Console.WriteLine($"Area: {shape.Area()}");
Console.WriteLine($"Perimeter: {shape.Perimeter}");
注意点
注意点
-
接口实现:
- 类可以实现多个接口,这使得 C# 支持多重继承。
- 接口的实现必须包括接口中声明的所有成员。
-
接口成员的访问修饰符:
- 接口中的所有成员默认为
public
,且接口成员不能有访问修饰符。 - 实现接口的类中的成员必须是
public
,以确保它们对外部代码可见。
- 接口中的所有成员默认为
-
接口不能包含实现:
- 接口不能包含方法体或字段,只能声明成员。
- 从 C# 8.0 开始,接口可以包含具有默认实现的方法,但这不是强制的。
-
接口的多重实现:
- 一个类可以实现多个接口,但接口之间不允许有任何成员实现的冲突。
- 当多个接口有相同的方法或属性时,类只需实现一次即可。
-
接口与抽象类的区别:
- 抽象类可以包含实现和状态(字段),而接口只能包含声明。
- 抽象类可以提供部分实现,并可以包含构造函数。
4.array
5.object
6.string
值传递和引用传递
值传递
系统首先为被调用方法的形参分配内存空间,并将实参的值按位置一一对应地复制给形参,此后,被调用方法中形参值得任何改变都不会影响到相应的实参。也就是会在栈上多创建一个相同的变量。
引用传递
系统不是将实参本身的值复制后传递给形参,而是将其引用值(即地址值)传递给形参,因此,形参所引用的该地址上的变量与传递的实参相同,方法体内相应形参值得任何改变都将影响到作为引用传递的实参
C#中参数传递 ref 与 out 的区别
1.ref
用途:允许在方法中修改参数的值,调用方法前参数必须已初始化
特点:ref可以把值传到方法里,也可以把值传到方法外
public void UpdateValue(ref int number)
{
number = 10;
}
int x = 5;
UpdateValue(ref x); // x 现在是 10
2.out
用途:用于方法中返回多个值,调用方法前参数不需要初始化,但必须在方法内被赋值.
特点:out只可以把值传到方法外
public void InitializeValue(out int number)
{
number = 10; // 必须赋值
}
int x;
InitializeValue(out x); // x 现在是 10
3.String特殊类型
符串赋值操作也是复制引用,但不改变原始字符串对象的内容
想在方法内改变一个字符串的内容并让这些更改反映到调用者那里,需要使用 ref
或 out
关键字