В чем разница между структурой и классом в .NET? - PullRequest
604 голосов
/ 16 августа 2008

В чем разница между структурой и классом в .NET?

Ответы [ 18 ]

4 голосов
/ 16 августа 2008

Ну, для начала, структура передается по значению, а не по ссылке. Структуры хороши для относительно простых структур данных, в то время как классы обладают гораздо большей гибкостью с архитектурной точки зрения благодаря полиморфизму и наследованию.

Другие, вероятно, могут дать вам больше деталей, чем я, но я использую структуры, когда структура, к которой я иду, проста.

3 голосов
/ 25 июля 2011
  1. Для событий, объявленных в классе, доступ + = и - = автоматически блокируется через блокировку (это), чтобы сделать их потокобезопасными (статические события блокируются для типа класса). События, объявленные в структуре, не имеют автоматически заблокированных доступа + = и - =. Блокировка (это) для структуры не будет работать, так как вы можете заблокировать только выражение ссылочного типа.

  2. Создание экземпляра структуры не может вызвать сборку мусора (если конструктор прямо или косвенно не создает экземпляр ссылочного типа), тогда как создание экземпляра ссылочного типа может вызвать сборку мусора.

  3. Структура всегда имеет встроенный открытый конструктор по умолчанию.

    class DefaultConstructor
    {
        static void Eg()
        {
            Direct     yes = new   Direct(); // Always compiles OK
            InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
            //...
        }
    }
    

    Это означает, что структура всегда реализуема, тогда как класс может не существовать, поскольку все его конструкторы могут быть частными.

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
    
  4. Структура не может иметь деструктора. Деструктор - это просто переопределение объекта. Финализованное скрытие, а структуры, являющиеся типами значений, не подлежат сборке мусора.

    struct Direct
    {
        ~Direct() {} // Compile-time error
    }
    class InDirect
    {
        ~InDirect() {} // Compiles OK
    }
    
    And the CIL for ~Indirect() looks like this:
    
    .method family hidebysig virtual instance void
            Finalize() cil managed
    {
      // ...
    } // end of method Indirect::Finalize
    
  5. Структура неявно запечатана, а класс - нет.
    Структура не может быть абстрактной, класс может.
    Структура не может вызвать: base () в своем конструкторе, тогда как класс без явного базового класса может.
    Структура не может расширять другой класс, класс может.
    Структура не может объявлять защищенные члены (например, поля, вложенные типы), которые может сделать класс.
    Структура не может объявлять члены абстрактной функции, абстрактный класс может.
    Структура не может объявлять члены виртуальной функции, класс может.
    Структура не может объявлять запечатанные члены-функции, класс может.
    Структура не может объявлять переопределение членов функции, класс может.
    Единственным исключением из этого правила является то, что структура может переопределять виртуальные методы System.Object, viz, Equals () и GetHashCode () и ToString ().

3 голосов
/ 23 сентября 2015

Как упоминалось ранее: классы являются ссылочным типом, а структуры - типами значений со всеми вытекающими.

В качестве основы правила Framework Design Guidelines рекомендует использовать Structs вместо классов, если:

  • Размер экземпляра не превышает 16 байт.
  • Логически представляет единственное значение, подобное примитивным типам (int, double и т. Д.)
  • Неизменен
  • Это не нужно будет часто в штучной упаковке
3 голосов
/ 29 мая 2014

Помимо основных различий в спецификаторе доступа и нескольких упомянутых выше, я хотел бы добавить некоторые из основных отличий, включая несколько упомянутых выше, с примером кода с выводом, который даст более четкое представление о ссылке и значении

Структуры:

  • Являются типами значений и не требуют выделения кучи.
  • Распределение памяти отличается и хранится в стеке
  • Полезно для небольших структур данных
  • Влияет на производительность, когда мы передаем значение методу, мы передаем всю структуру данных и все передается в стек.
  • Конструктор просто возвращает само значение структуры (обычно во временной папке в стеке), и это значение затем копируется по мере необходимости
  • Каждая из переменных имеет свою собственную копию данных, и операции с одной из них не могут повлиять на другую.
  • Не поддерживают указанное пользователем наследование, и они неявно наследуются от объекта типа

Класс:

  • Значение типа ссылки
  • Хранится в куче
  • Хранить ссылку на динамически размещенный объект
  • Конструкторы вызываются оператором 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: 10

Метод внутренней структуры Значение метода Inside объекта Struct: 20

После вызова метода значение объекта Struct равно: 10

Начальное значение объекта класса: 10

Метод внутри класса Внутреннее значение метода класса объекта: 20

После вызова метода значение объекта класса равно: 20

Здесь вы можете четко увидеть разницу между вызовом по значению и вызовом по ссылке.

2 голосов
/ 26 июля 2018
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                       |                                                Struct                                                |                                               Class                                               |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| Type                  | Value-type                                                                                           | Reference-type                                                                                    |
| Where                 | On stack / Inline in containing type                                                                 | On Heap                                                                                           |
| Deallocation          | Stack unwinds / containing type gets deallocated                                                     | Garbage Collected                                                                                 |
| Arrays                | Inline, elements are the actual instances of the value type                                          | Out of line, elements are just references to instances of the reference type residing on the heap |
| Aldel Cost            | Cheap allocation-deallocation                                                                        | Expensive allocation-deallocation                                                                 |
| Memory usage          | Boxed when cast to a reference type or one of the interfaces they implement,                         | No boxing-unboxing                                                                                |
|                       | Unboxed when cast back to value type                                                                 |                                                                                                   |
|                       | (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) |                                                                                                   |
| Assignments           | Copy entire data                                                                                     | Copy the reference                                                                                |
| Change to an instance | Does not affect any of its copies                                                                    | Affect all references pointing to the instance                                                    |
| Mutability            | Should be immutable                                                                                  | Mutable                                                                                           |
| Population            | In some situations                                                                                   | Majority of types in a framework should be classes                                                |
| Lifetime              | Short-lived                                                                                          | Long-lived                                                                                        |
| Destructor            | Cannot have                                                                                          | Can have                                                                                          |
| Inheritance           | Only from an interface                                                                               | Full support                                                                                      |
| Polymorphism          | No                                                                                                   | Yes                                                                                               |
| Sealed                | Yes                                                                                                  | When have sealed keyword                                                                          |
| Constructor           | Can not have explicit parameterless constructors                                                     | Any constructor                                                                                   |
| Null-assignments      | When marked with nullable question mark                                                              | Yes (+ When marked with nullable question mark in C# 8+)                                          |
| Abstract              | No                                                                                                   | When have abstract keyword                                                                        |
| Access Modifiers      | public, private, internal                                                                            | public, protected, internal, protected internal, private protected                                |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
1 голос
/ 16 августа 2008

Структуры являются фактическим значением - они могут быть пустыми, но никогда не равными нулю

Это правда, однако также обратите внимание, что с .NET 2 структуры поддерживают Nullable версию, а C # предоставляет некоторый синтаксический сахар для облегчения использования.

int? value = null;
value  = 1;
1 голос
/ 18 декабря 2017

Существует один интересный случай головоломки «класс против структуры» - ситуация, когда вам нужно вернуть несколько результатов из метода: выбрать, какой использовать. Если вы знаете историю ValueTuple - вы знаете, что ValueTuple (структура) был добавлен, потому что он должен быть более эффективным, чем Tuple (класс). Но что это значит в цифрах? Два теста: один - это struct / class, который имеет 2 поля, другой - с struct / class, который имеет 8 полей (с размерностью более 4 - класс должен стать более эффективным, чем struct с точки зрения тактов процессора, но, конечно, следует учитывать и загрузку GC ).

P.S. Еще один эталон для конкретного случая «sturct или class with collection»: https://stackoverflow.com/a/45276657/506147

BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT


            Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
  TestStructReturn |  Clr |     Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns |    4 | 0.0127 |      40 B |
   TestClassReturn |  Clr |     Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns |    5 | 0.0229 |      72 B |
 TestStructReturn8 |  Clr |     Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns |    8 | 0.0127 |      40 B |
  TestClassReturn8 |  Clr |     Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns |    6 | 0.0305 |      96 B |
  TestStructReturn | Core |    Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns |    1 | 0.0127 |      40 B |
   TestClassReturn | Core |    Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns |    2 | 0.0229 |      72 B |
 TestStructReturn8 | Core |    Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns |    7 | 0.0127 |      40 B |
  TestClassReturn8 | Core |    Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns |    3 | 0.0305 |      96 B |

Проверка кода:

using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;

namespace Benchmark
{
    //[Config(typeof(MyManualConfig))]
    [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkStructOrClass
    {
        static TestStruct testStruct = new TestStruct();
        static TestClass testClass = new TestClass();
        static TestStruct8 testStruct8 = new TestStruct8();
        static TestClass8 testClass8 = new TestClass8();
        [Benchmark]
        public void TestStructReturn()
        {
            testStruct.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn()
        {
            testClass.TestMethod();
        }


        [Benchmark]
        public void TestStructReturn8()
        {
            testStruct8.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn8()
        {
            testClass8.TestMethod();
        }

        public class TestStruct
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestClass
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestStruct8
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }

        public class TestClass8
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }
    }
}
0 голосов
/ 02 декабря 2011

Каждая переменная или поле типа примитивного значения или типа структуры содержит уникальный экземпляр этого типа, включая все его поля (публичные и приватные). Напротив, переменные или поля ссылочных типов могут иметь значение null или могут ссылаться на объект, хранящийся в другом месте, на который также может существовать любое количество других ссылок. Поля структуры будут храниться в том же месте, что и переменная или поле этого типа структуры, которое может быть либо в стеке, либо может быть частью другого объекта кучи.

Создание переменной или поля типа примитивного значения создаст его со значением по умолчанию; Создание переменной или поля типа структуры создаст новый экземпляр, создавая все поля в нем способом по умолчанию. Создание нового экземпляра ссылочного типа начнется с создания в нем всех полей способом по умолчанию и последующего запуска необязательного дополнительного кода в зависимости от типа.

Копирование одной переменной или поля типа примитива в другое приведет к копированию значения. Копирование одной переменной или поля типа структуры в другое приведет к копированию всех полей (открытых и закрытых) первого экземпляра во второй. Копирование одной переменной или поля ссылочного типа в другой приведет к тому, что последняя будет ссылаться на тот же экземпляр, что и первый (если есть).

Важно отметить, что в некоторых языках, таких как C ++, семантическое поведение типа не зависит от того, как он хранится, но это не относится к .NET. Если тип реализует семантику изменяемых значений, копирование одной переменной этого типа в другую копирует свойства первого в другой экземпляр, на который ссылается второй, и использование элемента второго для его изменения приведет к изменению этого второго экземпляра , но не первый. Если тип реализует изменяемую ссылочную семантику, копирование одной переменной в другую и использование элемента второй для изменения объекта будет влиять на объект, на который ссылается первая переменная; типы с неизменяемой семантикой не допускают мутации, поэтому семантически не имеет значения, создает ли копирование новый экземпляр или создает другую ссылку на первый.

В .NET типы значений могут реализовывать любую из вышеуказанных семантик при условии, что все их поля могут действовать аналогичным образом. Однако ссылочный тип может реализовывать только изменяемую ссылочную семантику или неизменяемую семантику; Типы значений с полями изменяемых ссылочных типов ограничены реализацией изменяемой ссылочной семантики или странной гибридной семантики.

...