В чем разница между const и readonly в C #?

1364

В чем разница между constи readonlyв C #?

Когда бы вы использовали один поверх другого?

Readonly
источник
Мне пришлось просмотреть несколько ответов, чтобы найти эту ссылку, но она хорошая. Взгляд Эрика Липперта на неизменность в C #
Фрэнк Брайс
2
@donstack, фактически в соответствии со ссылкой на C # , поле только для чтения может быть назначено и переназначено несколько раз в пределах объявления поля и конструктора.
Маркиз

Ответы:

1289

Помимо очевидного различия

  • необходимость объявлять значение во время определения значений constVS readonlyможет быть вычислена динамически, но должна быть назначена до выхода из конструктора .. после этого он будет заморожен.
  • 'const's неявно static. Вы используете ClassName.ConstantNameнотацию для доступа к ним.

Есть небольшая разница. Рассмотрим класс, определенный в AssemblyA.

public class Const_V_Readonly
{
  public const int I_CONST_VALUE = 2;
  public readonly int I_RO_VALUE;
  public Const_V_Readonly()
  {
     I_RO_VALUE = 3;
  }
}

AssemblyBссылки AssemblyAи использует эти значения в коде. Когда это скомпилировано,

  • в случае constзначения это похоже на поиск-замену, значение 2 «запекается» в AssemblyBIL. Это означает, что если завтра я обновлю I_CONST_VALUEдо 20 в будущем. AssemblyBбудет еще 2, пока я не перекомпилирую .
  • в случае readonlyзначения это похоже refна ячейку памяти. Значение не запекается вAssemblyB IL. Это означает, что если ячейка памяти обновляется, AssemblyBполучает новое значение без перекомпиляции. Так что, если I_RO_VALUEобновление до 30, вам нужно только построить AssemblyA. Все клиенты не должны быть перекомпилированы.

Поэтому, если вы уверены, что значение константы не изменится, используйте a const.

public const int CM_IN_A_METER = 100;

Но если у вас есть константа, которая может измениться (например, точность) или, если вы сомневаетесь, используйте readonly .

public readonly float PI = 3.14;

Обновление: Аку нужно получить упоминание, потому что он указал это первым. Также мне нужно подключить, где я это узнал .. Эффективный C # - Билл Вагнер

Gishu
источник
77
staticТочка , кажется, самый важный и полезный пункт -consts are implicitly static
LCJ
28
Часть о ссылочных значениях является наиболее важной. Значения констант могут быть оптимизированы.
CodingBarfield
22
readonlyпеременные могут быть изменены вне конструктора (отражение). Только компилятор пытается помешать вам изменить переменную вне конструктора.
Bitterblue
12
readonlyПеременные @ mini-me не могут быть изменены после завершения конструктора, даже через отражение. Время выполнения не обеспечивает этого. Время выполнения также не означает, что вы не измените string.Emptyего "Hello, world!", но я все равно не буду утверждать, что это делает string.Emptyмодифицируемым, или что код не должен предполагать, что string.Emptyэто всегда будет строка нулевой длины.
7
blogs.msmvps.com/jonskeet/2014/07/16/… интересна только для чтения накладные расходы только на чтение
CAD bloke
275

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

Ака
источник
8
При декомпиляции (Reflector, ILSpy, ..) на константу НИКОГДА НИКОГДА не ссылается ни один, независимо от того, какая сборка или другая сборка, поэтому вы вообще не можете анализировать использование константы в скомпилированном коде.
springy76
159

Константы

  • Константы по умолчанию являются статическими
  • Они должны иметь значение во время компиляции (вы можете иметь, например, 3.14 * 2, но не можете вызывать методы)
  • Может быть объявлено в функциях
  • Копируются в каждую сборку, которая их использует (каждая сборка получает локальную копию значений)
  • Может использоваться в атрибутах

Только для чтения полей

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

Статические поля только для чтения

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

Чтобы добавить, ReadOnly только для ссылочных типов делает ссылку только для чтения, а не для значений. Например:

public class Const_V_Readonly
{
  public const int I_CONST_VALUE = 2;
  public readonly char[] I_RO_VALUE = new Char[]{'a', 'b', 'c'};

  public UpdateReadonly()
  {
     I_RO_VALUE[0] = 'V'; //perfectly legal and will update the value
     I_RO_VALUE = new char[]{'V'}; //will cause compiler error
  }
}

источник
Есть ли другой ссылочный тип, stringкоторый вы не могли бы использовать в качестве константы?
springy76
Вы можете иметь constсо ссылочными типами, отличными от строки, но константа может иметь только значение null.
Майк Рософт,
40

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

РЕДАКТИРОВАТЬ: см. Гишу Гишу выше для тонкой разницы

Винко Врсалович
источник
32

const: Нельзя нигде изменить.

readonly: Это значение может быть изменено только в конструкторе. Не может быть изменено в обычных функциях.

Deepthi
источник
26

Есть маленькая ошибка с readonly. Поле только для чтения может быть установлено несколько раз в конструкторе (ах). Даже если значение задано в двух разных цепочечных конструкторах, оно все равно разрешено.


public class Sample {
    private readonly string ro;

    public Sample() {
        ro = "set";
    }

    public Sample(string value) : this() {
        ro = value; // this works even though it was set in the no-arg ctor
    }
}
Майк Два
источник
26

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

public class MyClass
{
    public const double PI1 = 3.14159;
}

readonlyЧлен как константа в том , что она представляет собой неизменное значение. Разница в том, что readonlyэлемент может быть инициализирован во время выполнения, в конструкторе, а также может быть инициализирован так, как он объявлен.

public class MyClass1
{
     public readonly double PI2 = 3.14159;

     //or

     public readonly double PI3;

     public MyClass2()
     {
         PI3 = 3.14159;
     }
}

Const

  • Они не могут быть объявлены как static (они неявно статичны)
  • Значение константы оценивается во время компиляции
  • константы инициализируются только при объявлении

только для чтения

  • Они могут быть как на уровне экземпляра, так и статическими.
  • Значение оценивается во время выполнения
  • readonly может быть инициализирован в объявлении или кодом в конструкторе
Sujit
источник
6
Они не могут быть статичными , они статичны. Вы должны дать понять, если вы имели в виду, что никто не может объявитьstatic const int i = 0;
nawfal
Можете ли вы объяснить, почему constобъявления не могут быть сделаны внутри методов?
Минь
21

Const - это константа времени компиляции, тогда как readonly позволяет вычислять значение во время выполнения и устанавливать его в конструкторе или инициализаторе поля. Таким образом, «const» всегда постоянен, но «только для чтения» доступен только для чтения после его назначения.

Эрик Липперт из команды C # имеет больше информации о различных типах неизменяемости

Wheelie
источник
15

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

Резюме :

  • Значение вашего const-свойства устанавливается во время компиляции и не может изменяться во время выполнения
  • Const не может быть помечен как статический - ключевое слово обозначает, что они статические, в отличие от полей только для чтения, которые могут.
  • Const не может быть ничем, кроме значений (примитивов)
  • Ключевое слово readonly помечает поле как неизменяемое. Однако свойство может быть изменено внутри конструктора класса
  • Ключевое слово только для чтения также можно комбинировать со статическим, чтобы оно действовало так же, как const (по крайней мере, на поверхности). Существует заметная разница, когда вы смотрите на IL между двумя
  • Поля const помечены как "буквальные" в IL, а readonly - "initonly"
Крис С
источник
11

Только чтение : значение может быть изменено через Ctor во время выполнения. Но не через функцию члена

Постоянная : по умолчанию статическая. Значение не может быть изменено из любого места (Ctor, Function, время выполнения и т. Д. Без указания места)

Йесин Абедин Сиам
источник
спасибо, что не заставили меня прочитать 4 параграфа только за эти два выноса ...
Дон Чидл
9

Еще одно замечание: значения, доступные только для чтения, могут быть изменены «хитрым» кодом с помощью отражения.

var fi = this.GetType()
             .BaseType
             .GetField("_someField", 
                       BindingFlags.Instance | BindingFlags.NonPublic);
fi.SetValue(this, 1);

Могу ли я изменить частное унаследованное поле только для чтения в C #, используя отражение?

Greg
источник
6

Я считаю, что constзначение является одинаковым для всех объектов (и должно быть инициализировано литеральным выражением), тогда как readonlyможет быть разным для каждого экземпляра ...

Дарен Томас
источник
5

Один из членов команды в нашем офисе предоставил следующие рекомендации о том, когда использовать const, static и readonly:

  • Используйте const, когда у вас есть переменная типа, который вы можете знать во время выполнения (string literal, int, double, enums, ...), что вы хотите, чтобы все экземпляры или потребители класса имели доступ к тому месту, где значение не должно изменяться.
  • Используйте статический когда у вас есть данные, которые вы хотите, чтобы все экземпляры или потребители класса имели доступ к тому, где значение может измениться.
  • Используйте статическое чтение только тогда, когда у вас есть переменная типа, который вы не можете знать во время выполнения (объекты), что вы хотите, чтобы все экземпляры или потребители класса имели доступ к тому месту, где значение не должно изменяться.
  • Используйте readonly, когда у вас есть переменная уровня экземпляра, которую вы знаете во время создания объекта, которая не должна изменяться.

И последнее замечание: поле const является статическим, но обратное неверно.

Скотт Лоуренс
источник
1
Я думаю, что вы имеете в виду "говорить". Обратное было бы «неконстантное поле не является статическим». Что может или не может быть правдой. И наоборот, «статическое поле (всегда) const» не соответствует действительности.
Майкл Блэкберн
5

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

Пример:

public static class Text {
  public const string ConstDescription = "This can be used.";
  public readonly static string ReadonlyDescription = "Cannot be used.";
}

public class Foo 
{
  [Description(Text.ConstDescription)]
  public int BarThatBuilds {
    { get; set; }
  }

  [Description(Text.ReadOnlyDescription)]
  public int BarThatDoesNotBuild {
    { get; set; }
  }
}
Hallgrim
источник
5
  • когда использовать constилиreadonly

    • const

      • константа времени компиляции : абсолютная константа, значение устанавливается во время объявления, находится в самом коде IL
    • readonly

      • постоянная времени выполнения : может быть установлена ​​в конструкторе / init через файл конфигурации, т.е. App.config, но после инициализации ее нельзя изменить
Райан Эфенди
источник
4

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

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

Клин
источник
Добавил, что в константах строго прописаны макросы #define. В противном случае мы можем отпугнуть всех людей на C или C ++. :-)
Джейсон Бейкер
4

ПОСТ

  1. Ключевое слово const может применяться к полям или локальным переменным
  2. Мы должны присвоить поле const во время объявления
  3. Память не выделена, поскольку значение const встраивается в сам код IL после компиляции. Это все равно, что найти все вхождения переменной const и заменить ее значением. Таким образом, код IL после компиляции будет иметь жестко запрограммированные значения вместо константных переменных.
  4. Const в C # по умолчанию являются статическими.
  5. Значение является постоянным для всех объектов
  6. Существует проблема с версионированием dll. Это означает, что всякий раз, когда мы изменяем открытую переменную или свойство const (фактически, это не должно изменяться теоретически), любая другая dll или сборка, которая использует эту переменную, должна быть пересобрана
  7. Только встроенные типы C # могут быть объявлены как константы
  8. Поле const не может быть передано как параметр ref или out

ReadOnly

  1. Ключевое слово readonly применяется только к полям, а не к локальным переменным
  2. Мы можем назначить поле только для чтения во время объявления или в конструкторе, а не в каких-либо других методах.
  3. динамическая память выделяется для полей только для чтения, и мы можем получить значение во время выполнения.
  4. Readonly принадлежит объекту, созданному таким образом, к которому можно получить доступ только через экземпляр класса. Чтобы сделать его членом класса, нам нужно добавить статическое ключевое слово перед чтением.
  5. Значение может отличаться в зависимости от используемого конструктора (так как оно принадлежит объекту класса)
  6. Если вы объявляете непримитивные типы (ссылочный тип) только для чтения, ссылка является неизменной, а не объектом, который она содержит.
  7. Поскольку значение получается во время выполнения, нет проблем с версионированием dll для полей / свойств только для чтения.
  8. Мы можем передать поле только для чтения в качестве параметров ref или out в контексте конструктора.
Мухаммед В.П.
источник
3

Еще одна ошибка .

Поскольку const действительно работает только с базовыми типами данных, если вы хотите работать с классом, вы можете почувствовать себя «вынужденными» использовать ReadOnly. Однако остерегайтесь ловушки! ReadOnly означает, что вы не можете заменить объект другим объектом (вы не можете заставить его ссылаться на другой объект). Но любой процесс, имеющий ссылку на объект, может свободно изменять значения внутри объекта!

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

Марк Т
источник
Да, это больше общая тема. Если у вас есть свойство get only, выставляющее arraylist, вы все равно можете изменить arraylist. Вы не можете установить другой arraylist для этого свойства, но вы не можете остановить пользователя от изменения arraylist.
Гишу
3

A constдолжен быть жестко закодирован , где это readonlyможет быть установлено в конструкторе класса.

Greg
источник
3

Существует заметная разница между полями const и readonly в C # .Net

const по умолчанию является статическим и должен быть инициализирован постоянным значением, которое нельзя изменить позже. Изменение значения также не допускается в конструкторах. Его нельзя использовать со всеми типами данных. Для экс-DateTime. Его нельзя использовать с типом данных DateTime.

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public readonly string Name = string.Empty; //No error, legal

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

class A
{
    public readonly int Id;

    public A(int i)
    {
        Id = i;
    }
}

Затем поле readonly может быть инициализировано мгновенными конкретными значениями, как показано ниже:

A objOne = new A(5);
A objTwo = new A(10);

Здесь экземпляр objOne будет иметь значение поля readonly, равное 5, а objTwo имеет значение 10. Это невозможно при использовании const.

Чираг
источник
2

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

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

Бретт Райан
источник
Я искренне сомневаюсь, что это правда ... Я пойду проверю.
СМЛ
это один из 50 конкретных способов улучшить ваш C # - amazon.co.uk/Effective-Specific-Ways-Improve-Your/dp/0321245660/…
Russ Cam
@ Андрей Харе - да, я только что проверил. Я очень удивлен, это настоящая ошибка, я действительно очень удивлен этим, поражен, что это так ...!
СМЛ
Я, однако, возражаю против использования здесь указателя слова. Это не указатель, это ссылка, и там есть разница в C # , как вы можете манипулировать неуправляемые указатели в небезопасном режиме , поэтому очень важно , чтобы различать между ними.
СМЛ
2

постоянная

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

Readonly

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

Подробнее об обоих объяснено здесь в этой статье

Викрам
источник
1

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

СМЛ
источник
1

Const и readonly похожи, но они не совсем одинаковы. Константное поле - это константа времени компиляции, что означает, что это значение может быть вычислено во время компиляции. Поле «только для чтения» включает дополнительные сценарии, в которых некоторый код должен выполняться во время создания типа. После создания поле только для чтения не может быть изменено.

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

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

так как значения как 3.14 и 0 являются константами времени компиляции. Тем не менее, рассмотрим случай, когда вы определяете тип и хотите предоставить некоторые заранее созданные экземпляры этого типа. Например, вы можете определить класс Color и предоставить «константы» для общих цветов, таких как черный, белый и т. Д. Это невозможно сделать с помощью константных членов, поскольку правые части не являются константами времени компиляции. Это можно сделать с помощью обычных статических членов:

public class Color
{
    public static Color Black = new Color(0, 0, 0);
    public static Color White = new Color(255, 255, 255);
    public static Color Red = new Color(255, 0, 0);
    public static Color Green = new Color(0, 255, 0);
    public static Color Blue = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) {
        red = r;
        green = g;
        blue = b;
    }
}

но тогда ничто не может помешать клиенту Color с этим взломать, возможно, путем замены значений Black и White. Излишне говорить, что это вызвало бы смятение у других клиентов класса Color. Функция «только для чтения» предназначена для этого сценария. Просто вводя ключевое слово readonly в объявлениях, мы сохраняем гибкую инициализацию, в то же время предотвращая перехват кода клиента.

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) {
        red = r;
        green = g;
        blue = b;
    }
}

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

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

public class A
{
    public static const C = 0;
}

и другой разработчик написал код, который опирался на A:

public class B
{
    static void Main() {
        Console.WriteLine(A.C);
    }
}

Теперь, может ли генерируемый код опираться на тот факт, что AC является константой времени компиляции? Т.е. можно ли просто заменить использование AC значением 0? Если вы скажете «да» на это, то это означает, что разработчик A не может изменить способ инициализации AC - это связывает руки разработчика A без разрешения. Если вы ответите «нет» на этот вопрос, то пропустите важную оптимизацию. Возможно, автор A уверен, что AC всегда будет нулевым. Использование const и readonly позволяет разработчику A указать намерение. Это улучшает поведение при управлении версиями, а также повышает производительность.

Рамеш Раджендран
источник
1

ReadOnly: значение будет инициализировано только один раз из конструктора класса.
const: может быть инициализирован в любой функции, но только один раз

donstack
источник
1

Разница заключается в том, что значение статического поля только для чтения устанавливается во время выполнения, поэтому оно может иметь различное значение для разных исполнений программы. Тем не менее, значение поля const устанавливается на постоянную времени компиляции.

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

Для получения подробной информации, пожалуйста, обратитесь к разделу Часто задаваемые вопросы по этой теме на C #: http://blogs.msdn.com/csharpfaq/archive/2004/12/03/274791.aspx

Йонатан Нир
источник
1

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

Омар АМЕЗУГ
источник
1

Const : Абсолютное постоянное значение в течение срока службы приложения.

Только для чтения : это может быть изменено во время выполнения.

Большие глаза
источник
1
Ваше определение «только для чтения», что оно может измениться, неверно. Я полагаю, под словом «изменить» вы подразумевали «установить», например, «оно может быть установлено во время выполнения».
Ахмед
0

Одна вещь, чтобы добавить к тому, что люди сказали выше. Если у вас есть сборка, содержащая значение только для чтения (например, readonly MaxFooCount = 4;), вы можете изменить значение, которое видят вызывающие сборки, отправив новую версию этой сборки с другим значением (например, только для чтения MaxFooCount = 5;)

Но с помощью const он будет свернут в код вызывающего, когда он будет скомпилирован.

Если вы достигли этого уровня владения C #, вы готовы к книге Билла Вагнера «Эффективное C #: 50 конкретных способов улучшить ваш C #», которая подробно отвечает на этот вопрос (и 49 других вещей).

Энтони
источник