Преимущества использования const вместо переменных внутри методов - PullRequest
69 голосов
/ 29 апреля 2011

Всякий раз, когда у меня есть локальные переменные в методе, ReSharper предлагает преобразовать их в константы:

// instead of this:
var s = "some string";
var flags = BindingFlags.Public | BindingFlags.Instance;

// ReSharper suggest to use this:
const string s = "some string";
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;

Учитывая, что это действительно постоянные значения (а не переменные), я понимаю, что ReSharper предлагает изменить их на const.

Но кроме этого, есть ли еще какое-то преимущество при использовании const (например, лучшая производительность), которое оправдывает использование const BindingFlags вместо удобного и удобочитаемого ключевого слова var?

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

Ответы [ 8 ]

82 голосов
/ 29 апреля 2011

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

Кроме того, обычно есть небольшое преимущество в производительности при использовании констант по сравнению с переменными,Это связано с тем, как они скомпилированы в MSIL, для этого журнала MSDN. Q & A :

Теперь, куда бы ни ссылались на myInt в коде, вместо того, чтобы делать«ldloc.0», чтобы получить значение из переменной, MSIL просто загружает постоянное значение, которое жестко закодировано в MSIL. Таким образом, использование констант обычно дает небольшое преимущество в производительности и памяти. Однако для их использования необходимо иметь значение переменной во время компиляции и любые ссылки на эту константу во время компиляции,даже если они находятся в другой сборке, будет выполнена подстановка.

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

20 голосов
/ 11 марта 2018

tl; dr для локальных переменных с литеральными значениями, const не имеет значения вообще.


Ваше различие между "внутренними методами" очень важно.Давайте посмотрим на это, затем сравним его с const полями.

Const локальные переменные

Преимущество * *1013* локальной переменной const в том, что значение не можетбыть переназначенным.

Однако const ограничен примитивными типами (int, double, ...) и string, что ограничивает его применимость.

Отступление: ЕстьЭто предложения для компилятора C #, позволяющие использовать более общую концепцию «только для чтения» ( здесь ), которая распространит это преимущество на другие сценарии.Они, вероятно, не будут рассматриваться как const, и, вероятно, будут иметь другое ключевое слово для таких объявлений (например, let или readonly var или что-то подобное).

Рассмотрим эти два метода:

private static string LocalVarString()
{
    var s = "hello";
    return s;
}

private static string LocalConstString()
{
    const string s = "hello";
    return s;
}

В режиме Release мы видим следующий (сокращенный) IL:

.method private hidebysig static string LocalVarString() cil managed 
{
    ldstr        "hello"
    ret          
}

.method private hidebysig static string LocalConstString() cil managed 
{
    ldstr        "hello"
    ret          
}

Как вы можете видеть, они оба производят одинаковый IL.Независимо от того, локальный s равен const или нет, это не влияет.

То же самое верно для примитивных типов.Вот пример использования int:

private static int LocalVarInt()
{
    var i = 1234;
    return i;
}

private static int LocalConstInt()
{
    const int i = 1234;
    return i;
}

И снова IL:

.method private hidebysig static int32 LocalVarInt() cil managed
{
    ldc.i4       1234
    ret          
}

.method private hidebysig static int32 LocalConstInt() cil managed
{
    ldc.i4       1234
    ret     
}

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

Поля констант

Сравнение поля const с переменным полем отличается от .Неконстантное поле должно читаться во время выполнения.Таким образом, вы получите IL следующим образом:

// Load a const field
ldc.i4       1234

// Load a non-const field
ldsfld       int32 MyProject.MyClass::_myInt

Понятно, как это может привести к разнице в производительности, если предположить, что JIT не может встроить само постоянное значение.

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

Const выражений

Рассмотримэти два объявления:

const int i = 1 + 2;
int i = 1 + 2;

Для формы const сложение должно быть вычислено во время компиляции, то есть число 3 сохраняется в IL.

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

C # 7.3Компилятор выдает ldc.i4.3 код операции для обоих приведенных выше выражений.

14 голосов
/ 29 апреля 2011

Согласно моему пониманию, значения Const не существуют во время выполнения - то есть в форме переменной, хранящейся в некотором месте памяти - они включаются в код MSIL во время компиляции.И, следовательно, будет иметь влияние на производительность.Более того, во время выполнения не потребовалось бы выполнять какие-либо домашние операции (проверки конверсии / сборка мусора и т. Д.), А также переменные, требующие этих проверок.

4 голосов
/ 29 апреля 2011

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

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

3 голосов
/ 29 апреля 2011

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

  1. Мне нужно инициализировать его значением прямо сейчас.я не могу сделать это в другом месте.
  2. Я не могу нигде изменить его значение.

В коде все о читаемости и коммуникации.

2 голосов
/ 29 апреля 2011

Значение const также «разделяется» между всеми экземплярами объекта. Это также может привести к снижению использования памяти.

Как пример:

public class NonStatic
{
    int one = 1;
    int two = 2;
    int three = 3;
    int four = 4;
    int five = 5;
    int six = 6;
    int seven = 7;
    int eight = 8;
    int nine = 9;
    int ten = 10;        
}

public class Static
{
    static int one = 1;
    static int two = 2;
    static int three = 3;
    static int four = 4;
    static int five = 5;
    static int six = 6;
    static int seven = 7;
    static int eight = 8;
    static int nine = 9;
    static int ten = 10;
}

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

    static void Main(string[] args)
    {
        var maxSize = 1000000;
        var items = new List<NonStatic>();
        //var items = new List<Static>();

        for (var i=0;i<maxSize;i++)
        {
            items.Add(new NonStatic());
            //items.Add(new Static());
        }

        Console.WriteLine(System.Diagnostics.Process.GetCurrentProcess().WorkingSet64);
        Console.Read();
    }

При использовании «NonStatic» рабочий набор составляет 69 398 528 по сравнению с 32 423 936 при использовании статического.

1 голос
/ 29 апреля 2011

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

Когда вы объявляете это, оно вроде «жестко закодировано» на промежуточном языке Microsoft (MSIL).

Хотя немного, это может улучшить производительность вашего кода. Если я объявляю переменную и могу сделать ее константой, я всегда делаю это. Не только потому, что это может улучшить производительность, но и потому, что это идея констант. Иначе, почему они существуют?

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

1 голос
/ 29 апреля 2011

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

...