目录
// 引用声明:本文部分内容及图片参考自 Solis, D.M., Illustrated C# 2012(Fourth Edition). 2013: 人民邮电出版社.
// 版权声明:支持原创,转载引用链接:https://blog.csdn.net/img_Guo/article/details/81751010
一、什么是结构
1. 定义:类似于类,结构是程序员自己定义的数据类型,它们可以有数据成员和函数成员。
2.与类区别:(1)类是引用类型而结构是值类型;(2)结构不能被派生,它是隐式密闭的;(3)结构可以也有实例构造函数和静态构造函数,但是结构不允许有析构函数。
/* 代码功能:看懂什么是结构
* 时间:2018年8月17日10点58分
*/
struct Coordinate //声明三维坐标结构,其声明与类声明类似
{
/*包含三个int类型公有字段*/
public int x;
public int y;
public int z;
}
class Program
{
static void Main(string[] args)
{
Coordinate f1, f2, f3;
f1.x = 1; f1.y = 2; f1.z = 3;
f2.x = 4; f2.y = 5; f2.z = 6;
f3.x = f1.x + f2.x;
f3.y = f1.y + f2.y;
f3.z = f1.z + f2.z;
Console.WriteLine("f1: {0}, {1}, {2}", f1.x, f1.y, f1.z);
Console.WriteLine("f2: {0}, {1}, {2}", f2.x, f2.y, f2.z);
Console.WriteLine("f3: {0}, {1}, {2}", f3.x, f3.y, f3.z);
}
}
/* 程序运行结果
--------------------------------
f1: 1, 2, 3
f2: 4, 5, 6
f3: 5, 7, 9
--------------------------------
*/
二、结构是值类型
与所有的值类型一样,结构类型变量有自己的数据。因此:
(1)两个结构变量不能引用同一个对象,因为值类型存储在栈的原因;
(2)结构类型的变量不能为null;
![](https://i-blog.csdnimg.cn/blog_migrate/f52f1af2169333f5dfad6d73c31049f1.png)
/* 代码功能:结构是值类型,类是引用类型,它们在内存中是如何分配的?
* 时间:2018年8月17日11点14分
*/
class Myclass
{
public int i;
public int j;
}
struct MyStruct
{
public int i;
public int j;
}
class Program
{
static void Main()
{
Myclass myClass = new Myclass();
MyStruct myStruct = new MyStruct();
}
}
三、给结构赋值
把一个结构实例赋值给另一个结构,就是将这个结构实例的值赋值给另一个结构。而复制类实例变量就不同,赋值类实例变量时只复制引用给另一个类的引用。
![](https://i-blog.csdnimg.cn/blog_migrate/f4cb44045d3f0eed324b05d444f2dde8.png)
/* 代码功能:类变量赋值和结构变量赋值
* 时间:2018年8月17日11点45分
*/
class Myclass
{
public int i;
public int j;
}
struct MyStruct
{
public int i;
public int j;
}
class Program
{
static void Main()
{
Myclass f1 = null,f2 = new Myclass();
MyStruct f3 = new MyStruct();
f2.i = 5; f2.j = 10; // 类变量赋值
f3.i = 10; f3.j = 15; //结构变量赋值
}
}
四、结构的“实例构造”与“静态构造”函数
结构可以有实例构造函数和静态构造函数,但是不允许有析构函数。
4.1 结构的实例构造函数
1. 语言隐式的为每一个结构都提供一个无参数构造函数,该构造函数把每结构的一个成员都设置该类型的默认值,即引用类型成员默认设置为null,值类型成员默认设置为该类型默认值;
2. 结构默认提供的无参数构造函数不能重定义、不能删除,但是可以创建额外的带参数的构造函数。(PS:这一点与类不同,对于类来说,编译器只有在没有其他构造函数声明的时候提供隐式的无参数构造函数);
3. 实例化结构对象:
(1)使用new运算符:结构创建对象时使用new调用一个构造函数以初始化每个成员值,包括隐式无参数构造函数,都需要使用new运算符(即使不在堆中分配内存);
(2)不使用new运算符:结构创建对象时不使用new运算符创建结构的实例,会有以下的限制:显示设置数据成员的值后才能使用它们的值,对所有数据赋值之后,才能调用任何的函数成员;
/* 代码功能:结构实例化对象时使用new和不使用new的两种情况
* 时间:2018年8月17日13点24分
*/
struct MyStruct
{
public int i;
public int j;
/* 与类不同,结构这里尽管声明了额外的带参数构造函数,
* 结构还是会隐式创建一个不能定义不能删除的构造函数。
*/
public MyStruct(int a, int b)
{
//结构里对所有数据赋值之后,才能调用任何的函数成员
i = a;
j = b;
}
public int Sum()
{
return i + j;
}
}
}
class Program
{
static void Main()
{
/* 使用new的情况(调用构造函数) */
MyStruct f1 = new MyStruct(); //调用隐式无参数构造函数,编译器会自动把每结构的一个成员都设置该类型的默认值
MyStruct f2 = new MyStruct(); //调用显式带参数构造函数
Console.WriteLine("结构实例化对象时使用new的情况:");
Console.WriteLine(" (1)调用无参数构造函数的情况:f1.i = {0}, f1.j = {1}", f1.i, f2.j);
Console.WriteLine(" (2)调用有参数构造函数的情况:f2.i = {0}, f2.j = {1}", f2.i, f2.j);
Console.WriteLine( ); //换行
/* 不使用new的情况(不调用构造函数) */
MyStruct f3;
f3.i = 5; f3.j = 10; //显示设置数据成员的值后才能使用它们的值
int sum = f3.Sum(); //对所有数据赋值之后,才能调用任何的函数成员
Console.WriteLine("结构实例化对象时不使用new的情况:");
Console.WriteLine(" 调用有参数构造函数的情况:f3.i = {0}, f3.j = {1},f3.Sum() = {2}", f2.i, f2.j,sum);
}
/* 程序运行结果
--------------------------------
结构实例化对象时使用new的情况:
(1)调用无参数构造函数的情况:f1.i = 0, f1.j = 0
(2)调用有参数构造函数的情况:f2.i = 0, f2.j = 0
结构实例化对象时不使用new的情况:
调用有参数构造函数的情况:f3.i = 0, f3.j = 0,f3.Sum() = 15
--------------------------------
*/
4.2 结构的静态构造函数
1. 功能:与类的静态构造函数功能相同,结构的静态构造函数创建并初始化静态数据成员,而不能引用任何实例成员;
2. 结构静态构造函数不能被显式调用,系统会自动调用,在:在调用显示声明的结构构造函数之前、在引用结构的静态成员之前;
3. 结构的静态构造函数与实例构造函数的相同点:静态构造函数的名称与结构名相同,且不能有返回值;
4. 结构的静态构造函数与实例构造函数的不同点:结构静态构造函数使用static声明;结构只能有一个不带参数的静态构造函数;结构静态构造函数不能有访问修饰符。
5. 如同静态方法,结构的静态构造函数不能访问所在结构的实例成员,因此不能使用this访问器。
/* 代码功能:结构的静态构造函数解析
* 时间:2018年8月17日14点01分
*/
struct MyStruct
{
public int i;
public int j;
public static int k; //静态成员
static MyStruct() //静态构造函数
{
k = 1;
}
public MyStruct(int a, int b) //带参数构造函数,注意结构还是会有默认的无参数实例构造函数
{
i = a;
j = b;
}
}
class Program
{
static void Main()
{
Console.WriteLine("初始化静态值:{0}",MyStruct.k); //注意静态成员在类外只能使用类名进行调用
MyStruct.k = 10;
Console.WriteLine("更改静态成员后的静态值k:{0}",MyStruct.k);
MyStruct myStruct = new MyStruct(); //这里创建对象是不会再调用静态构造改值的
Console.WriteLine("创建另外结构对象后的静态值k:{0}", MyStruct.k);
}
}
/* 程序运行结果
--------------------------------
初始化静态值:1
更改静态成员后的静态值k:10
创建另外结构对象后的静态值k:10
--------------------------------
*/
五、结构作为返回值和参数
1. 返回值:当结构作为返回值时,将创建它的副本并从函数成员返回。
2. 值参数:结构作为值参数时,将创建实参结构的副本,该副本用于方法的执行中。
3. ref 和 out 参数:如果把一个结构用作 ref 和 out 参数,传入方法的是该结构的一个引用,这样可以修改其数据成员。
六、结构与类的其它区别
1. 与类定义不同,在定义结构时字段初始化语句是不允许的;
struct MyStruvt
{
public int i;
public int j = 10; //error! 结构中字段不允许初始化
}
2. 结构是隐式密闭的,因此结构不能被继承。
所以只有类中的new关键字、override修饰符可以用于结构,而protecred、internal、abstract、virtual成员修饰符不能用于结构。
3. 装箱和拆箱:
如果想将一个结构作为引用类型对象,必须进行装箱,装箱的过程就是制作值类型变量的引用类型副本。
4. 对结构进行分配比创建类的实例开销小,所以用结构代替类有的时候会提高性能,但是必须考虑到装箱、拆箱的高代价。
5. 预定义的简单类型(int、short等),尽管再C#和.NET中被视为原始类型,但是它们实际上再.NET中都被实现为结构。
6. 可以使用与分布类相同的方法声明分布结构。
7. 结构和类一样,可以实现接口。