Вопрос

В чем разница между struct и class в .NET?

Это было полезно?

Решение

В .NET существует две категории типов, ссылочные типы и типы значений.

Структуры - это типы значений и классы - это ссылочные типы.

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

Переменная , содержащая тип значения содержит весь тип значения ценность.Для структуры это означает, что переменная содержит всю структуру целиком, со всеми ее полями.

Переменная , содержащая ссылочный тип содержит указатель, или ссылка в другое место в памяти, где находится фактическое значение.

Начнем с того, что в этом есть одно преимущество:

  • типы значений всегда содержит значение
  • ссылочные типы может содержать ноль-ссылка, означающая, что в данный момент они вообще ни на что не ссылаются

Внутренне, ссылочный типs реализуются как указатели, и, зная это и то, как работает присваивание переменных, существуют и другие поведенческие паттерны:

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

Когда вы объявляете переменные или поля, вот чем отличаются эти два типа:

  • переменная: тип значения живет в стеке, ссылочный тип хранится в стеке как указатель на какое-то место в памяти кучи, где находится фактическая память (хотя обратите внимание Серия статей Эрика Липпертса:Стек - это Деталь реализации.)
  • класс/структурное поле: тип значения живет полностью внутри этого типа, ссылочный тип находится внутри типа как указатель на какое-то место в памяти кучи, где находится фактическая память.

Другие советы

Краткое изложение каждого из них:

Только классы:

  • Может поддерживать наследование
  • Являются ссылочными (указательными) типами
  • Ссылка может быть нулевой
  • Есть накладные расходы на память для каждого нового экземпляра

Только структуры:

  • Не удается поддерживать наследование
  • Являются типами значений
  • Передаются по значению (например, целые числа)
  • Не может иметь нулевую ссылку (если не используется значение Nullable)
  • У вас нет накладных расходов памяти на каждый новый экземпляр - если только они не "упакованы в коробку".

Как классы, так и структуры:

  • Обычно ли составные типы данных используются для содержания нескольких переменных, имеющих некоторую логическую взаимосвязь
  • Может содержать методы и события
  • Может поддерживать интерфейсы

В .NET объявления struct и class различают ссылочные типы и типы значений.

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

Когда вы передаете тип значения, каждое из них является копией.Весь код работает над своей собственной копией.

Это можно показать на примере:

struct MyStruct 
{
    string MyProperty { get; set; }
}

void ChangeMyStruct(MyStruct input) 
{ 
   input.MyProperty = "new value";
}

...

// Create value type
MyStruct testStruct = new MyStruct { MyProperty = "initial value" }; 

ChangeMyStruct(testStruct);

// Value of testStruct.MyProperty is still "initial value"
// - the method changed a new copy of the structure.

Для класса это было бы по-другому

class MyClass 
{
    string MyProperty { get; set; }
}

void ChangeMyClass(MyClass input) 
{ 
   input.MyProperty = "new value";
}

...

// Create reference type
MyClass testClass = new MyClass { MyProperty = "initial value" };

ChangeMyClass(testClass);

// Value of testClass.MyProperty is now "new value" 
// - the method changed the instance passed.

Классы могут быть ничем - ссылка может указывать на null.

Структуры - это фактическое значение - они могут быть пустыми, но никогда null.По этой причине структуры всегда имеют конструктор по умолчанию без параметров - им нужно "начальное значение".

От Microsoft Выбор между классом и структурой ...

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

РАССМОТРИМ структуру вместо класса:

  • Если экземпляры данного типа невелики и обычно недолговечны или обычно встроены в другие объекты.

X ИЗБЕГАЙТЕ структуры если только тип не имеет ВСЕ из следующих характеристик:

  • Он логически представляет собой единственное значение, аналогичное примитивным типам (int, double и т.д.).
  • Размер его экземпляра составляет менее 16 байт.
  • Это непреложно. (не может быть изменен)
  • Его не нужно будет часто упаковывать в коробку.

В дополнение ко всем различиям, описанным в других ответах:

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

Если вам нужно видео, объясняющее все различия, вы можете ознакомиться Часть 29 - Учебное пособие по C # - Разница между классами и структурами в C#.

Экземпляры классов хранятся в управляемой куче.Все переменные, "содержащие" экземпляр, являются просто ссылкой на экземпляр в куче.Передача объекта методу приводит к передаче копии ссылки, а не самого объекта.

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

Различие усиливается благодаря именам C ++ / CLI:"ref class" - это класс, описанный первым, "value class" - это класс, описанный вторым.Ключевые слова "класс" и "структура", используемые в C #, - это просто то, что необходимо выучить.

Разница между структурами и классами:

  • Структуры - это тип значения принимая во внимание , что Классы являются ссылочным типом.
  • Структуры хранятся в стеке принимая во внимание , что Классы хранятся в куче.
  • Типы значений сохраняют свое значение в памяти, где они объявлены, но ссылочный тип содержит ссылку на память объекта.
  • Типы значений немедленно уничтожаются после потери области видимости, тогда как ссылочный тип только переменной destroy после потери области видимости. Объект позже уничтожается сборщиком мусора.
  • Когда вы копируете struct в другую структуру, создается новая копия этой структуры изменение одной структуры не повлияет на значение другой структуры.
  • Когда вы копируете класс в другой класс, он копирует только ссылочную переменную .
  • Обе ссылочные переменные указывают на один и тот же объект в куче.Изменение одной переменной повлияет на другую ссылочную переменную.
  • Структуры не могут иметь деструкторов, но классы могут иметь деструкторы.
  • Структуры не могут иметь явных конструкторов без параметров принимая во внимание, что класс can structs не поддерживает наследование, но классы поддерживают.Оба варианта поддерживают наследование от интерфейса.
  • Конструкции герметичного типа.

Структура против класса

Структура - это тип значения, поэтому она хранится в стеке, но класс является ссылочным типом и хранится в куче.

Структура не поддерживает наследование и полиморфизм, но класс поддерживает и то, и другое.

По умолчанию все члены структуры являются общедоступными, но члены класса по умолчанию являются частными по своей природе.

Поскольку структура является типом значения, мы не можем присвоить null объекту struct, но это не относится к классу.

Просто для полноты картины, есть еще одно отличие при использовании Equals метод, который наследуется всеми классами и структурами.

Допустим, у нас есть класс и структура:

class A{
  public int a, b;
}
struct B{
  public int a, b;
}

и в основном методе у нас есть 4 объекта.

static void Main{
  A c1 = new A(), c2 = new A();
  c1.a = c1.b = c2.a = c2.b = 1;
  B s1 = new B(), s2 = new B();
  s1.a = s1.b = s2.a = s2.b = 1;
}

Тогда:

s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false

Итак, структуры подходят для числовых объектов, таких как точки (за исключением координат x и y).И занятия подходят для других.Даже если у 2 человек одинаковое имя, рост, вес ..., они все равно остаются 2 людьми.

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

  • При использовании структуры внутри содержащего ее класса выделяется память для хранения данных.
  • В случае с классом содержащий класс будет просто содержать указатель на новый класс в другой области памяти.

Это также верно и для массивов, поэтому массив структур в памяти выглядит следующим образом

[struct][struct][struct][struct][struct][struct][struct][struct]

Где в виде массива классов выглядит примерно так

[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]

Фактические значения, которые вас интересуют, на самом деле хранятся не в массиве, а в другом месте памяти.

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

Самое медленное, что делает современный процессор, - это не обработка чисел, это извлечение данных из памяти, а попадание в кэш L1 происходит во много раз быстрее, чем чтение данных из оперативной памяти.

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

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

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

Структуры:

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

Класс:

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

Внутренний метод Struct Внутреннее значение метода объекта Struct равно:20

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

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

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

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

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

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

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

  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. Структура не может иметь деструктора.Деструктор - это просто переопределение object .Скрытая доработка, а структуры, являющиеся типами значений, не подлежат сборке мусора.

    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, а именно Equals(), и GetHashCode(), и toString() .

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

В качестве примера из Руководства по разработке фреймворка правил рекомендуется использовать Структуры вместо классов, если:

  • Размер его экземпляра составляет менее 16 байт
  • Он логически представляет собой единственное значение, аналогичное примитивным типам (int, double и т.д.).
  • Это непреложно
  • Его не придется часто упаковывать в коробку
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                       |                                                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                                |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+

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

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

int? value = null;
value  = 1;

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

P.S.Есть еще один ориентир для конкретного случая "sturct или класс с коллекциями": 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;
            }
        }
    }
}

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top