.NET中的结构和类有什么区别?


当前回答

结构和类别之间的差异:

结构是值类型,而类是引用类型。结构存储在堆栈上,而类存储在堆值类型将其值保存在声明它们的内存中,但引用类型保存对内存中对象的引用。值类型在作用域丢失后立即销毁,而引用类型仅在作用域丢失后销毁变量。该对象随后被垃圾收集器销毁。将结构复制到另一个结构时,该结构的新副本创建。修改的结构不会影响其他结构。将一个类复制到另一个类时,它只复制参考变量。两个引用变量都指向堆上的同一对象。对一个变量所做的更改将影响另一个参考变量。结构不能有析构函数,但类可以有析构器。结构不能具有显式无参数构造函数,而类可以。结构不支持继承,但类支持支持从接口继承。结构为密封型。

其他回答

每个项目的简短摘要:

仅限班级:

可以支持继承是引用(指针)类型引用不能为空每个新实例的内存开销

仅结构:

无法支持继承是值类型按值传递(如整数)不能有空引用(除非使用了Nullable)每个新实例没有内存开销-除非“装箱”

类和结构:

复合数据类型通常用于包含一些具有某种逻辑关系的变量吗可以包含方法和事件可以支持接口

基元值类型或结构类型的每个变量或字段都持有该类型的唯一实例,包括其所有字段(公共和私有)。相比之下,引用类型的变量或字段可能为空,或者可能引用存储在其他位置的对象,其他引用也可能存在。结构的字段将存储在与该结构类型的变量或字段相同的位置,该变量或字段可能位于堆栈上,也可能是另一个堆对象的一部分。

创建原始值类型的变量或字段将使用默认值创建它;创建结构类型的变量或字段将创建一个新实例,以默认方式创建其中的所有字段。创建引用类型的新实例将首先以默认方式创建其中的所有字段,然后根据类型运行可选的附加代码。

将一个基本类型的变量或字段复制到另一个将复制值。将结构类型的一个变量或字段复制到另一个变量将前一个实例的所有字段(公共和私有)复制到后一个实例。将一个引用类型的变量或字段复制到另一个将导致后者引用与前者相同的实例(如果有的话)。

需要注意的是,在某些语言(如C++)中,类型的语义行为与它的存储方式无关,但在.NET中则不是这样。如果一个类型实现了可变值语义,则将该类型的一个变量复制到另一个变量会将第一个实例的财产复制到第二个实例所引用的另一个实例,并且使用第二个实例的成员来变异它将导致第二个例子被改变,而不是第一个。如果一个类型实现了可变的引用语义,那么将一个变量复制到另一个变量并使用第二个变量的成员来变异对象将影响第一个变量所引用的对象;具有不可变语义的类型不允许变异,因此复制是创建新实例还是创建对第一个实例的另一个引用在语义上无关紧要。

在.NET中,值类型可以实现上述任何语义,前提是它们的所有字段都可以这样做。然而,引用类型只能实现可变引用语义或不可变语义;具有可变引用类型字段的值类型限于实现可变引用语义或奇怪的混合语义。

如前所述:类是引用类型,而结构是具有所有后果的值类型。

根据经验,框架设计指南建议在以下情况下使用结构而不是类:

它的实例大小小于16字节它逻辑上表示单个值,类似于原始类型(int、double等)它是不可变的它不必经常装箱

除了访问说明符的基本区别,以及上面提到的几个区别之外,我还想添加一些主要区别,包括上面提到的一些区别,以及带有输出的代码示例,这将提供对引用和值的更清晰的了解

结构:

是值类型,不需要堆分配。内存分配不同,存储在堆栈中适用于小型数据结构影响性能,当我们将值传递给方法时,我们会传递整个数据结构,所有数据都传递给堆栈。构造函数只返回结构值本身(通常在堆栈上的临时位置),然后根据需要复制该值每个变量都有自己的数据副本,其中一个变量的操作不可能影响另一个变量。不支持用户指定的继承,它们隐式继承自类型对象

类别:

参考类型值存储在堆中存储对动态分配对象的引用构造函数是用new运算符调用的,但它不会在堆上分配内存多个变量可以引用同一对象对一个变量的操作可能会影响另一个变量引用的对象

代码示例

    static void Main(string[] args)
    {
        //Struct
        myStruct objStruct = new myStruct();
        objStruct.x = 10;
        Console.WriteLine("Initial value of Struct Object is: " + objStruct.x);
        Console.WriteLine();
        methodStruct(objStruct);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Struct Object is: " + objStruct.x);
        Console.WriteLine();

        //Class
        myClass objClass = new myClass(10);
        Console.WriteLine("Initial value of Class Object is: " + objClass.x);
        Console.WriteLine();
        methodClass(objClass);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Class Object is: " + objClass.x);
        Console.Read();
    }
    static void methodStruct(myStruct newStruct)
    {
        newStruct.x = 20;
        Console.WriteLine("Inside Struct Method");
        Console.WriteLine("Inside Method value of Struct Object is: " + newStruct.x);
    }
    static void methodClass(myClass newClass)
    {
        newClass.x = 20;
        Console.WriteLine("Inside Class Method");
        Console.WriteLine("Inside Method value of Class Object is: " + newClass.x);
    }
    public struct myStruct
    {
        public int x;
        public myStruct(int xCons)
        {
            this.x = xCons;
        }
    }
    public class myClass
    {
        public int x;
        public myClass(int xCons)
        {
            this.x = xCons;
        }
    }

输出

Struct Object的初始值为:10

内部结构方法Struct对象的内部方法值为:20

结构对象的方法调用值为:10

类对象的初始值为:10

内部类方法类对象的内部方法值为:20

类对象的方法调用值为:20

在这里,您可以清楚地看到按值调用和按引用调用之间的区别。

除了其他答案之外,还有一个基本的区别值得注意,那就是数据如何存储在数组中,因为这会对性能产生重大影响。

对于结构,数组包含结构的实例对于类,数组包含指向内存中其他位置的类实例的指针

因此,内存中的结构数组如下所示

[结构][结构]〔结构〕〔结构〕

而类数组看起来像这样

指针

对于一个类数组,您感兴趣的值不会存储在数组中,而是存储在内存的其他位置。

对于绝大多数应用程序来说,这种差异并不重要,但是,在高性能代码中,这将影响内存中数据的位置,并对CPU缓存的性能产生很大影响。在可以/应该使用结构的情况下使用类将大大增加CPU上的缓存未命中数。

现代CPU做的最慢的事情不是处理数字,而是从内存中提取数据,一级缓存命中速度比从RAM读取数据快很多倍。

下面是一些可以测试的代码。在我的机器上,遍历类数组所需的时间大约是结构数组的3倍。

    private struct PerformanceStruct
    {
        public int i1;
        public int i2;
    }

    private class PerformanceClass
    {
        public int i1;
        public int i2;
    }

    private static void DoTest()
    {
        var structArray = new PerformanceStruct[100000000];
        var classArray = new PerformanceClass[structArray.Length];

        for (var i = 0; i < structArray.Length; i++)
        {
            structArray[i] = new PerformanceStruct();
            classArray[i] = new PerformanceClass();
        }

        long total = 0;
        var sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < structArray.Length; i++)
        {
            total += structArray[i].i1 + structArray[i].i2;
        }

        sw.Stop();
        Console.WriteLine($"Struct Time: {sw.ElapsedMilliseconds}");
        sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < classArray.Length; i++)
        {
            total += classArray[i].i1 + classArray[i].i2;
        }

        Console.WriteLine($"Class Time: {sw.ElapsedMilliseconds}");
    }