《C#图解教程》拾遗十一-结构

本文深入探讨C#中的结构(struct),讲解其作为值类型的特点、赋值行为、构造函数的使用,以及在性能上的考量。结构与类的主要区别在于结构是值类型,赋值时会复制整个对象,而非引用。此外,结构不能继承,但可以实现接口。了解这些细节有助于更好地利用结构进行代码优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、什么是结构

二、结构是值类型

三、对结构赋值

四、构造函数和析构函数

1、实例构造函数

2、静态构造函数

3、构造函数和析构函数小结

五、属性和字段初始化语句

六、结构是密封的

七、装箱和拆箱

八、结构作为返回值和参数

九、关于结构的更多内容


一、什么是结构

结构是程序员定义的数据类型,与类非常类似,它们有数据成员和函数成员,虽然与类相似,但是结构有许多重要的区别,最重要的区别是:

  • 类是引用类型,而结构是值类型;
  • 结构是隐式密封的,这意味着不能从它们派生其他结构;

声明结构的语法与声明类相似:

关键字
  ⬇
struct StructName
{
    MemberDeclarations
}
struct Point
{
    public int X;
    public int Y;
}
class Program
{
    static void Main(string[] args)
    {
        Point first, second, third;

        first.X = 10;first.Y = 10;
        second.X = 20;second.Y = 20;
        third.X = first.X + second.X;
        third.Y = first.Y + second.Y;

        Console.WriteLine($"first: {first.X},{first.Y}");
        Console.WriteLine($"second: {second.X},{second.Y}");
        Console.WriteLine($"third: {third.X},{third.Y}");
    }
}

执行结果:

二、结构是值类型

和所有值类型一样,结构类型变量含有自己的数据,因此:

  • 结构类型的变量不能为null;
  • 两个结构变量不能引用同一对象;
class CSimple
{
    public int X;
    public int Y;
}
struct Simple
{
    public int X;
    public int Y;
}
class Program
{
    static void Main()
    {
        CSimple cs = new CSimple();
        Simple ss = new Simple();
        ...
    }
}

上述代码声明了一个名为CSimple的类和一个名为Simple的结构,当为它们各自声明一个变量时,下图展示了这两个变量的内存安排:

三、对结构赋值

把一个结构赋值给另一个结构,就是将一个结构的值复制给另一个结构,这和复制类变量不同,复制类变量时只复制引用。

class CSimple
{
    public int X;
    public int Y;
}
struct Simple
{
    public int X;
    public int Y;
}
class _3_AssignValueToStruct
{
    static void Main()
    {
        CSimple cs1 = new CSimple(), cs2 = null;            //类实例
        Simple ss1 = new Simple(), ss2 = new Simple();      //结构实例

        cs1.X = ss1.X = 5;          //将5赋值给ss1.X和cs1.X
        cs1.Y = ss1.Y = 10;         //将10赋值给ss1.Y和cs1.Y

        cs2 = cs1;                  //赋值类实例
        ss2 = ss1;                  //赋值结构实例
    }
}

上图展示了类变量赋值和结构变量赋值的区别。

注意,在类赋值之后,cs2和cs1指向堆中同一对象,但在结构赋值之后,ss2的成员的值和ss1的相同。

四、构造函数和析构函数

结构可以有实例构造函数和静态构造函数,但不允许有析构函数。

1、实例构造函数

语言隐式地为每个结构提供一个无参数的构造函数,这个构造函数把结构的每个成员设置为该类型的默认值,值成员设置成它们地默认值,引用成员设置成null。

对于每个结构,都存在预定义的无参构造函数,而且不能删除或重定义。但是,可以创建另外的构造函数,只要它们有参数。注意,这和类不同,对于类,编译器只在没有声明其他构造函数时提供隐式的无参构造函数。

调用一个构造函数,包括饮食无参数构造函数,要使用new运算符,注意,即使不从堆中分配内存,也要使用new运算符。

struct Simple
{
    public int X;
    public int Y;
    public Simple(int a,int b)      //带有参数的构造函数
    {
        X = a;
        Y = b;
    }
}
class _4_1InstantConstructure
{
    static void Main()
    {
        Simple s1 = new Simple();           //调用隐式构造函数
        Simple s2 = new Simple(5, 10);      //调用构造函数

        Console.WriteLine($"{s1.X},{s1.Y}");
        Console.WriteLine($"{s2.X},{s2.Y}");
    }
}

也可以不适用new运算符创建结构的实例,然而,如果这样做,有如下限制:

  • 在显式设置数据成员之后,才能使用它们的值;
  • 在对所有数据成员赋值之后,才能调用结构的函数成员;
struct Simple
{
    public int X;
    public int Y;
}
class _4_12InstantConstructure
{
    static void Main()
    {
        Simple s1, s2;  //没有构造函数的调用
        //Console.WriteLine("{0},{1}", s1.X, s1.Y);   //还未被赋值,编译错误,使用了可能未赋值的字段

        s2.X = 5;
        s2.Y = 10;
        Console.WriteLine($"{s2.X},{s2.Y}");    //没问题
    }
}

2、静态构造函数

与类相似,结构的静态构造函数创建并初始化静态数据成员,并且不能引用实例成员。结构的静态构造函数遵从与类的静态构造函数一样的规则,但允许有不带参数的静态构造函数。

以下两种行为,任意一种发生之前,将会调用静态构造函数:

  • 调用显式声明的构造函数;
  • 引用结构的静态成员;

3、构造函数和析构函数小结

五、属性和字段初始化语句

在声明结构体时,不允许使用实例属性和字段初始化语句:

struct Simple
{
    public int x = 0;    //编译错误
    public int y = 10;   //编译错误

    public int prop1 {get; set;} = 5;    //编译错误
}

但是,结构体的静态属性和静态字段都可以在声明结构体时进行初始化,即使结构体本身不是静态的。

六、结构是密封的

结构总是隐式密封的,因此,不能从它们派生其他结构。

由于结构不支持继承,个别类成员修饰符用在结构成员上将没有意义,因此不能在结构成员声明中使用。不能用于结构的修饰符如下:

  • protected
  • protected internal
  • abstract
  • sealed
  • virtual

结构本身派生自System.ValueType,而System.ValueType派生自object。

两个可用于结构成员并与继承相关的关键字是new和override修饰符,当创建一个和基类System.ValueType的成员同名的成员时可使用它们,所有结构都派生自System.ValueType。

七、装箱和拆箱

如同其他值类型数据,如果想将一个结构实例作为引用类型对象,必须创建装箱的副本。装箱的过程就是制作值类型变量的引用类型副本。

八、结构作为返回值和参数

结构可以用作返回值和参数:

  • 返回值        当结构作为返回值时,将创建它的副本并从函数成员返回;
  • 值参数        当结构被用作值参数时,将创建实参结构的副本,该副本用于方法的执行中;
  • ref和out参数        如果把一个结构用作ref或out参数,传入方法的是该结构的一个引用,这样就可以修改其数据成员;

九、关于结构的更多内容

对结构进行分配的开销比创建类实例小,所以使用结构代替类有时可以提高性能,但要注意装箱和拆箱的高昂代价。

关于结构,需要知道的最后一些事情如下:

  • 预定义简单类型(int、short、long等)尽管在.net和C#中被视为原始类型,但它们实际上在.net中都实现为结构;
  • 可以使用与声明分部类相同的方法声明分部结构;
  • 结构和类一样,可以实现接口;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值