Вопрос по c#, coding-style, resharper, const – Разве ваше второе предложение не совсем противоположно тому, что @BrokenGlass написал в своем ответе?

68

й раз, когда у меня есть локальные переменные в методе, 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 ключевое слово?

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

Ваш Ответ

8   ответов
81

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

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

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

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

@SACO, если вы беспокоитесь о производительности, вам следует проверить сборки релизов. Даже если компилятор Mono запускает операцию сложения, JIT, скорее всего, применяет базовое постоянное сгибание к машинному коду. Рослин в C # 7.3правильно делает. Drew Noakes
По крайней мере, для моно это не всегда так. Проверено с моно 5.14.0.177. Локальные константы будут вызывать при использовании констант для вычисления некоторого значения (например, const int a = 10; const int b = a + 20;) различные операторы. ldc.r4 110 вместо ldc.i4.s и других операций для фактического вычисления значений. (протестировано с Debug в качестве цели, все еще нахожу его релевантным, особенно для высокопроизводительного кода; для низкой производительности разница может быть не столь значимой) SACO
@ Дрю Ноакс хорошо! И отличная ссылка, которой вы поделились. SACO
@SACO не забудьте проверитьJIT Asm view слишком. Drew Noakes
Для локальных переменных при использовании нет никакого выигрыша в производительностиconst, Это не дает компилятору никакой дополнительной информации. Проверьтеэтот ответ Больше подробностей. Drew Noakes
14

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

Для кода, приведенного в исходном вопросе, нет разницы сгенерированного IL, и, следовательно, нет разницы с памятью, проверкой, сборкой мусора и т. Д. Drew Noakes
плюс 1 за указание проверки конверсии / сборки мусора. Christian Mark
@Liam - согласен, я попытался объяснить это в следующем предложении. «Значения Const не существуют во время выполнения -то есть в форме переменной, хранящейся в некоторой ячейке памяти" YetAnotherUser
Значения Const не существуют во время выполнения немного вводит в заблуждение. Они существуют, иначе вы не сможете их использовать. Liam
2

то также может привести к снижению использования памяти.

В качестве примера:

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 при использовании статического.

@DrewNoakes - я не уверен, что полностью понимаю, но я расширил свой ответ. Пожалуйста, дайте мне знать, если я допустил ошибку или упустил вашу точку зрения. Rob P.
Я предоставил больше информации вэтот ответ. Drew Noakes
Единственный тип ссылки, который вы можете использовать сconst являетсяstring, Обе строки const (const string s = "foo";) и строковый литерал (var s = "foo";) будет интернирован. Так что здесь нет никакой разницы в потреблении памяти. Все остальные значения const передаются по значению. Drew Noakes
Static отличается от const и не имеет ничего общего с исходным вопросом, извините. Я думаю, что ваша редакция не помогла вашему ответу. На самом деле вы не можете выполнить тест по N элементам с помощью const, потому что в итоге вы получите один экземпляр в памяти, как если бы вы использовали строку времени компиляции. Например, строковый литерал"hello" будут интернированы, поэтому два метода, которые определяют"hello" поскольку локальные переменные в конечном итоге будут использовать одну и ту же ссылку (на основе того, что строки являются неизменяемыми). тем не мение"hello".ToUpper() является значением времени выполнения, поэтому несколько вызовов приведут к нескольким экземплярам. Drew Noakes
3

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

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

В коде все о читабельности и общении.

1

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

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

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

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

Разве ваше второе предложение не совсем противоположно тому, что @BrokenGlass написал в своем ответе? M4N
1

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

На самом деле, нет никакой разницы в производительности или памяти в случае локальной переменной. Видетьмой ответ. Drew Noakes
15

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

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

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

только польза от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;
}

И снова ИЛ:

.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 опкод для обоих приведенных выше выражений.

@YuryKozlov нет никакой разницы вообще. Посмотрите на IL. Drew Noakes
Не могу согласиться с вашим утверждением, что «для локальных переменных с литеральными значениями const вообще не имеет значения». Они имеют смысл, если используются в методе с ранним возвратом и условными выражениями. Например, если вы объявляете переменную в начале вашего метода и используете ее только в if (), тогда const не будет брать ни одного байта из памяти, пока вы не введете оператор if (в то время как обычная переменная в любом случае съедает несколько байтов). Yury Kozlov
Предположим, что вы занимались микрооптимизацией, имеет ли смысл определять константы, используемые в локальных методах, как поля внутри класса, а затем ссылаться на них внутри методов? Я предполагаю, что это будет за счет читабельности, а также сделает эти методы менее «автономными» (инкапсулированными). Dan Diplo
@DanDiplo, если его значение не имеет значения, где вы его определяете. Значение const заканчивается выражением непосредственно в IL. Drew Noakes
4

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

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

Я не верю утверждению о сокращении памяти здесь, чтобы быть правдой.const работает только с примитивами (в результате чего IL, который, как вы говорите, загружает константы в стек оценки) и со строками (которые выделяют одинаковый объем памяти независимо от того, являются ли они константами или нет). Drew Noakes
Провел несколько тестов инет никакой разницы во время выполнения в память или что-нибудь еще. Сгенерированный код идентичен. Drew Noakes

Похожие вопросы