C#学习笔记 -- 结构
1、什么是结构
结构是开发者定义的数据类型, 与类非常相似, 他们有数据成员和函数成员, 虽然与类很相似, 但是有区别
-
类是引用类型, 而结构是值类型
-
结构是隐式密封的, 他们不能派生出其他类
struct StructName
{
Member
}
例子
struct Point1101
{
public int X;
public int Y;
}
static void Main(string[] args)
{
//1101
{
Point1101 first;
first.X = 10; first.Y = 10;
Point1101 second;
second.X = 30; second.Y = 20;
Point1101 third;
third.X = 30; third.Y = 30;
Console.WriteLine($"点1: {first.X}, {first.Y}");
Console.WriteLine($"点2: {second.X}, {second.Y}");
Console.WriteLine($"点3: {third.X}, {third.Y}");
}
}
2、结构是值类型
所有值类型引用, 结构类型变量含有自己的数据, 因此
-
结构类型的变量不能为null
-
两个结构变量不能引用同一个对象
-
结构的字段值只存在与栈中
class CSimple
{
public int X;
public int Y;
}
struct Simple
{
public int X;
public int Y;
}
class Program
{
static void Main(string[] args)
{
CSimple cs = new CSimple();
Simple ss = new Simple();
}
}
3、对结构赋值
把一个结构赋值给另一个结构, 就是将一个结构的值复制给另一个结构
-
在类赋值之后, cs2和cs1执行堆中同一个对象,
-
但是结构赋值之后, ss2的成员值和ss1的相同
class CSimple
{
public int X;
public int Y;
}
struct Simple
{
public int X;
public int Y;
}
static void Main(string[] args)
{
CSimple cs1 = new CSimple();
Simple ss1 = new Simple();
CSimple cs2 = null;
Simple ss2 = new Simple();
cs1.X = ss1.X = 5;
cs1.Y = ss1.Y = 10;
cs2 = cs1;
ss2 = ss1;
}
4、构造函数和析构函数
结构可以有实例构造函数和静态构造函数, 但是不能有析构函数
(1)实例构造函数
-
语言隐式的为每个结构提供一个无参数的构造函数
-
这个构造函数把结构的每个成员设置为该类型的默认值
-
值成员设置成他们的默认值
-
引用成员设置为null
-
-
对于每个结构, 都存在预定义的无参构造器, 并且不能删除或重定义,
-
但是可以创建另外的构造函数, 只要他们有参数, 与类是不同的, 对于类, 编译器只在没有声明其他构造函数时提供隐式的无参构造
-
调用构造器, 结构也需要使用new运算符, 即时不从堆中分配内存
struct Simple { public int X; public int Y; public Simple(int x, int y) { X = x; Y = y; } }
Simple simple = new Simple(5, 10);
-
也可不用new, 限制如下
-
显式设置数据成员之后, 才能使用他们的值
-
在堆所有数据成员赋值之后, 才能调用结构的函数成员
-
struct Simple
{
public int X;
public int Y;
}
static void Mian(string[] args)
{
Simple s1;
Simple s2;
Console.WriteLine($"{s1.X}, {s1.Y}"); //编译错误
s2.X = 5;
s2.Y = 10;
Console.WriteLine($"{s1.X}, {s1.Y}"); //没问题
Console.WriteLine($"{s2.X}, {s2.Y}"); //没问题
}
(2)静态构造函数
与类相似, 结构的静态构造函数创建并初始化静态数据成员, 不能引用实例成员, 结构的静态构造器遵从与类的静态构造器一样的规则, 但允许有不带参数的静态构造器
以下两种行为, 任意一种发生之前, 将会调用静态构造函数
-
调用显示声明的构造函数
-
引用结构的静态成员
(3)小结
类型 | 描述 |
---|---|
无参实例构造 | 不能在程序中声明. 系统为所有结构提供一个隐式的构造函数, 不能被程序删除或重定义 |
有参实例构造 | 可以在程序中声明 |
静态构造函数 | 可以在程序中声明 |
析构函数 | 不允许声明 |
5、属性和字段初始化语句
-
在结构体声明中, 不允许使用实例属性和字段初始化语句
-
但是结构体的静态属性和静态字段都可以声明结构体时进行初始化, 即使结构体本身不是静态的
struct Simple
{
public int X = 0; //编译错误
public int Y = 19; //编译错误
public int MyProp { set; get; } = 5; //编译错误
}
6、结构是密封的
-
结构总是隐式密封, 不能派生其他结构
-
由于不支持继承, 个别类修饰符用在结构成员上没有意义, 因此不能在结构成员声明中使用
-
protected
-
protected internal
-
abstract
-
sealed
-
virtual
-
-
结构本身派生自
System.ValueType
而System.ValueType
派生自object -
两个可以用于结构成员并与继承相关的关键字是
new
和override
,System.ValueType
的成员同名成员可以使用他们
7、装箱和拆箱
将一个结构实例作为引用类型对象必须装修, 反之是拆箱
8、结构作为返回值和参数
-
返回值
-
当结构作为返回值时, 将创建他的副本并从函数成员返回
-
-
值参数
-
当结构被用作值参数时, 将创建实参结构的副本. 该副本用于方法的执行中
-
-
ref 和 out参数
-
如果把一个结构用作ref或out参数, 传入方法的是该结构的一个引用, 这样就可以修改其数据成员
-
9、其他
-
给结构进行分配的开销比创建类实例小, 所以使用结构替代类有时候可以提高性能, 但要注意拆箱、装箱的代价
-
预定义简单类型, 尽管在doNet、C#中被视为原始类型, 但是实际上在doNET中都实为结构
-
可以使用与声明分部类相同的方法声明分部结构
-