Преимущества использования const вместо переменных внутри методов


всякий раз, когда у меня есть локальные переменные в методе, 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 всегда предлагает мне сделать const string вместо string, но я думаю, что это больше о полях класса, где мой вопрос касается локальной переменной/consts.

8 64

8 ответов:

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

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

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

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

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

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


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

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

The только пользу const локальная переменная заключается в том, что значение не может быть переназначено.

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

отступление: есть предложения для компилятора C#, чтобы позволить более общее понятие' readonly ' locals (здесь), который распространит это преимущество на другие сценарии. Они, вероятно, не будут рассматриваться как 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 fields

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

// Load a const field
ldc.i4       1234

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

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

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

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

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

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

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

в коде его все о читаемости и связи.

значение 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();
    }

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

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

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

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

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

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