Практические различия между классами и структурами в .NET (не концептуальный)?

Каждый раз, когда я пытался искать о различиях между классами и структурами в C# или .NET, я закончил с концептуальным обзором этих двух вещей как тип значения или ссылочный тип, где переменные выделяются и т.д., Но мне нужны некоторые практические различия. Я нашел, что некоторым нравится другое поведение оператора присваивания, имея конструкторов и т.д. Кто-либо может обеспечить некоторые более практические различия, которые будут непосредственно полезны при кодировании? Как вещи работает с одной, но не с другой или та же операция, показывающая другое поведение. И некоторые частые ошибки относительно этих двух.

Также предложите, где рассмотреть использование структуры вместо класса. И где структуры не должны использоваться.

Править: Я должен вызвать конструктора явно или просто объявление, что переменная типа структуры будет достаточна? (Я должен сделать это новым вопросом?)

6
задан S.L. Barth - Reinstate Monica 17 July 2012 в 16:59
поделиться

7 ответов

Структуры в контейнере могут быть изменены только в том случае, если контейнер является встроенным массивом:

struct Point { public int x, y; void Move(int dx, int dy) { x += dx; y += dy; } }
...
Point[] points = getPointsArray();
points[0].Move(10, 0) = 10;
// points[0].x is now 10 higher.
List<Point> points = getPointsList();
points[0].Move(10, 0);
// No error, but points[0].x hasn't changed.

По этой причине я решительно поддерживаю неизменяемые структуры:

Point Move(int dx, int dy) { return new Point(x + dx, y + dy); }
...
points[0] = points[0].Move(10, 0); // Always works.

Общее наблюдение: классы обычно лучше. Структуры превосходят, когда вы хотите содержать небольшие концептуально атомарные структуры данных, такие как Point, Complex (число), Rational и т. Д.

2
ответ дан 8 December 2019 в 18:33
поделиться

Иногда вы не хотите, чтобы передаваемые вами данные были изменяемыми, а поскольку изменяемая структура может быть просто злом, Я бы не стал его создавать :) Вот пример ситуации:

class Версия:

class AccountInfo {
   public string OwnerName { get; set; }
   public string AccountNumber { get; set; }
}

struct Версия:

struct AccountInfo {
   public string OwnerName;
   public string AccountNumber;
}

А теперь представьте, что вы вызвали такой метод:

public bool TransferMoney(AccountInfo from, AccountInfo to, decimal amount)
{
   if(!IsAuthorized(from)) return false;
   //Transfer money
}

struct является типом Value , что означает, что в метод передается копия .Версия класса означает, что в метод передается ссылка , вы бы не хотели, например, чтобы номер учетной записи можно было изменить после прохождения авторизации, вы не хотите, чтобы в такой операции ничего не менялось ... вам нужен неизменяемый тип значения. Здесь возникает еще один вопрос, почему изменяемые структуры являются злом ... любая операция, при которой вы не хотите, чтобы на что-либо повлияло изменение ссылочного объекта, была бы практическим местом, где структура может подходят лучше.

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

1
ответ дан 8 December 2019 в 18:33
поделиться

structs, поскольку они являются типами значений, копируются при присвоении; если вы создаете свой собственный struct, вы должны сделать его неизменяемым, см. Почему изменяемые structs - зло?

1
ответ дан 8 December 2019 в 18:33
поделиться

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

Но самое важное практическое отличие, с которым вы столкнетесь, когда впервые решите заменить свой класс на struct, заключается в том, что struct передаются по значению, в то время как экземпляры классов передаются по ссылке.

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

Например, если у вас есть структура с именем MyStruct и класс с именем MyClass, и вы передаете их в этот метод:

 void DoSomething(MyStruct str, MyClass cls)
 {
      // this will change the copy of str, but changes
      // will not be made to the outside struct
      str.Something = str.Something + 1;

      // this will change the actual class outside
      // the method, because cls points to the
      // same instance in memory
      cls.Something = cls.Something + 1;
 }

когда метод завершится, свойство вашего класса будет увеличено, но свойство вашего struct останется неизменным, потому что переменная str внутри метода DoSomething не указывает на одно и то же место в памяти.

0
ответ дан 8 December 2019 в 18:33
поделиться

Единственное важное практическое различие состоит в том, что структуры являются типами значений, тогда как классы - ссылочными типами. Это имеет несколько последствий.

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

struct X
{
    public int ID;
    public string Name;
}

X x1 = new X { ID = 1, Name = "Foo" };
X x2 = x1;
x2.Name = "Bar";
Console.WriteLine(x1.Name);    // Will print "Foo"

class Y
{
    public int ID;
    public string Name;
}
Y y1 = new Y { ID = 2, Name = "Bar" };
Y y2 = y1;
y2.Name = "Baz";
Console.WriteLine(y1.Name);    // Will print "Baz"

X и Y абсолютно одинаковы, за исключением того, что X является struct. Результаты этого отличаются, потому что каждый раз, когда мы присваиваем X, создается копия, и если мы изменяем копию, то мы не изменяем оригинал. С другой стороны, когда мы присваиваем содержимое y1 в y2, мы лишь копируем ссылку; и y1, и y2 ссылаются на физически один и тот же объект в памяти.

Вторым следствием того, что структуры являются типами значений, являются родовые ограничения. Если вы хотите передать типы значений, имя ограничения будет буквально "struct" или "class":

public class MyGeneric<T>
    where T : struct
{ ... }

Вышеприведенное позволит вам создать MyGeneric или MyGeneric, но не MyGeneric. С другой стороны, если мы изменим его на where T : struct, нам больше не разрешено создавать ни один из первых двух вариантов, но MyGeneric - можно.

И последнее, но не менее важное: вам нужно использовать структуры при написании interop, потому что с помощью структур вы можете гарантировать определенное расположение в памяти.

0
ответ дан 8 December 2019 в 18:33
поделиться

Ссылка, предоставленная Tejs ( http://www.jaggersoft.com/pubs/StructsVsClasses.htm ), является хорошим объяснением (хотя и немного устарела, особенно в отношении объяснения событий. ).

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

Рассмотрим следующий код:

struct NumberStruct
{
   public int Value;
}

class NumberClass
{
   public int Value = 0;
}

class Test
{
   static void Main() 
   {
      NumberStruct ns1 = new NumberStruct();
      NumberStruct ns2 = ns1;
      ns2.Value = 42;

      NumberClass nc1 = new NumberClass();
      NumberClass nc2 = nc1;
      nc2.Value = 42;

      Console.WriteLine("Struct: {0}, {1}", ns1.Value, ns2.Value);
      Console.WriteLine("Class: {0}, {1}", nc1.Value, nc2.Value);
   }
}

Поскольку оба ns1 и ns2 относятся к типу значений NumberStruct , каждый из них имеет собственное место хранения, поэтому присвоение ns2.Number не влияет на значение ns1.Number . Однако, поскольку nc1 и nc2 являются ссылочными типами, присвоение nc2.Number действительно влияет на значение nc1.Number , потому что они оба содержат одну и ту же ссылку.

[Отказ от ответственности: приведенный выше код и текст взяты из Sams Teach Yourself Visual C # 2010 за 24 часа ]

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

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

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

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

0
ответ дан 8 December 2019 в 18:33
поделиться

Вот интересная ссылка: http://www.jaggersoft.com/pubs/StructsVsClasses.htm

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

-1
ответ дан 8 December 2019 в 18:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: