const
и readonly
похожи, но они не совсем одинаковы.
Поле const
является константой времени компиляции, что означает, что это значение может быть вычислено во время компиляции. Поле readonly
включает дополнительные сценарии, в которых некоторый код должен выполняться во время конструирования типа. После построения поле 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, green, blue) = (r, g, b);
}
Но тогда ничто не может помешать клиенту Color изменить его, возможно, путем замены значений черного и белого. Излишне говорить, что это вызвало бы смятение у других клиентов класса 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, green, blue) = (r, g, b);
}
Интересно отметить, что константные члены всегда статичны, тогда как член только для чтения может быть статическим или нет, как обычное поле.
Можно использовать одно ключевое слово для этих двух целей, но это приводит либо к проблемам с версиями, либо к проблемам с производительностью. Предположим на минуту, что мы использовали одно ключевое слово для этого (const), и разработчик написал:
public class A
{
public static const C = 0;
}
и другой разработчик написал код, основанный на A:
public class B
{
static void Main() => Console.WriteLine(A.C);
}
Теперь, может ли генерируемый код основываться на том факте, что A.C является константой времени компиляции? Т.е. можно ли просто использовать A.C значением 0? Если вы скажете «да» на это, то это означает, что разработчик A не может изменить способ инициализации A.C - это связывает руки разработчика A без разрешения.
Если вы ответите «нет» на этот вопрос, то важная оптимизация будет пропущена. Возможно, автор A уверен, что A.C всегда будет нулевым. Использование как const, так и readonly позволяет разработчику A указать намерение. Это улучшает поведение при управлении версиями и повышает производительность.