Практические различия между классами и структурами в .net (не концептуально)? - PullRequest
6 голосов
/ 01 мая 2010

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

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

Edit: Нужно ли явно вызывать конструктор или достаточно просто объявить переменную типа структуры? (Должен ли я задать новый вопрос?)

Ответы [ 8 ]

10 голосов
/ 24 сентября 2010

ОК, вот несколько конкретных практических отличий:

  • Переменная может иметь значение null , если это class , но никогда не может иметь значение null, если это struct .

  • default(T) равен null для класса , но для структуры фактически создает значение (состоящее из множества двоичных нулей).

  • A struct может быть обнулено с помощью Nullable<T> или T?. класс нельзя использовать для T in Nullable<T> или T?.

  • A struct всегда имеет открытый конструктор по умолчанию (конструктор с нулевыми параметрами). Программист не может переопределить этот конструктор с помощью пользовательской реализации - он в основном «установлен в камень». class позволяет программисту не иметь конструктора по умолчанию (или частного).

  • Поля в классе могут иметь объявленные по умолчанию значения. В struct они не могут.

  • A класс может наследоваться от другого класса, но struct не может быть объявлена ​​производной от чего-либо (она неявно происходит от System.ValueType).

  • Имеет смысл использовать class в object.ReferenceEquals(), но использование переменной struct всегда приведет к false.

  • Имеет смысл использовать class в операторе lock(), но использование переменной struct вызовет очень тонкий сбой. Код не будет заблокирован.

  • В 32-битной системе теоретически можно выделить массив до 536 870 912 ссылок на класс , но для struct Вы должны принять во внимание размер структуры, потому что вы выделяете фактические экземпляров .

2 голосов
/ 01 мая 2010

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

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 и т. Д.

1 голос
/ 01 мая 2010

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

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

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

1 голос
/ 01 мая 2010

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

0 голосов
/ 01 мая 2010

Предоставленная ссылка 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 научите себя Visual C # 2010 за 24 часа ]

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

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

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

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

0 голосов
/ 01 мая 2010

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

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

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<int> или MyGeneric<X>, но не MyGeneric<Y>. С другой стороны, если мы изменим его на where T : struct, нам больше не разрешено создавать какие-либо из первых двух, но MyGeneric<Y> нормально.

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

0 голосов
/ 01 мая 2010

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

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

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

Например, если у вас есть структура с именем 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;
 }

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

0 голосов
/ 01 мая 2010

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

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

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