Разница между константами и полями только для чтения в C#

Const

Как с помощью констант так и с помощью полей только для чтения (readonly fields) можно добиться одной и той же цели — инициализировать поле обьекта при его создании неким значением, которое гарантированно не будет изменено при дальнейшей жизни этого обьекта. Но эти два способа сильно отличаются друг от друга. В этой статье я попытаюсь доступно рассказать про эти отличия.

Статичность

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

public class PhysicalConstants
        {
            public const double pi = 3.14;
            //public static const double g = 9.8; // Ошибка: константа не может быть помечена ключевым словом static!

            public readonly int c = 299792458; // На уровне экземпляра
            public static readonly double h = 6.63E-34; // На уровне типа
        }

Момент задания значения

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

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

static void Main(string[] args)
        {
            Console.WriteLine(PhysicalConstants.pi); // Выводит: 3.14
            Console.WriteLine(PhysicalConstants.g); // Выводит: 9.8
            Console.WriteLine(PhysicalConstants.h); // Выводит: 6.63E-34
            //Console.WriteLine(PhysicalConstants.c); // Так мы сделать не можем, поскольку это поле является экземплярным
            PhysicalConstants pc = new PhysicalConstants();
            Console.WriteLine(pc.c); // Выводит: 0
            Console.ReadLine();
        }

        public class PhysicalConstants
        {
            public const double pi = 3.14; // Значение константы задаем прямо при ее инициализации
            public const double g = 9.8;

            public readonly int c = 299792458; // Мы определили значение здесь, но его еще можно поменять в конструторе
            public static readonly double h; // Это значение мы зададим в статическом конструкторе

            // Значение полей для чтения можно задать в конструкторах
            public PhysicalConstants()
            {
                // Обратите внимание, что поле будет иметь именно значение 0,
                // так как оно затирает inline инициализацию
                c = 0;                
            }

            static PhysicalConstants()
            {
                // Так как это поле статическое, то определяем его именно тут
                h = 6.63E-34;
            }
        }

Проблема версий

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

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

Типы полей

Константы могут иметь только такие типы, которые ваш компилятор воспринимает как примитивные. В C# следующие типы являются примитивными и могут быть использованы для определения констант: Boolean, Char, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64,UInt64, Single, Double, Decimal, и String. Тем не менее, C# также позволяет определить константу не примитивного типа, но при условии, что ей будет присвоено значение null.

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>