96

Whenever I have local variables in a method, ReSharper suggests to convert them to constants:

// 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;

Given that these are really constant values (and not variables) I understand that ReSharper suggest to change them to const.

But apart from that, is there any other advantage when using const (e.g. better performance) which justifies using const BindingFlags instead of the handy and readable var keyword?

BTW: I just found a similar question here: Resharper always suggesting me to make const string instead of string, but I think it is more about fields of a class where my question is about local variable/consts.

Community
  • 1
  • 1
M4N
  • 94,805
  • 45
  • 217
  • 260

8 Answers8

95

The compiler will throw an error if you try to assign a value to a constant, thus possibly preventing you from accidentally changing it.

Also, usually there is a small performance benefit to using constants vs. variables. This has to do with the way they are compiled to the MSIL, per this MSDN magazine Q&A:

Now, wherever myInt is referenced in the code, instead of having to do a "ldloc.0" to get the value from the variable, the MSIL just loads the constant value which is hardcoded into the MSIL. As such, there's usually a small performance and memory advantage to using constants. However, in order to use them you must have the value of the variable at compile time, and any references to this constant at compile time, even if they're in a different assembly, will have this substitution made.

Constants are certainly a useful tool if you know the value at compile time. If you don't, but want to ensure that your variable is set only once, you can use the readonly keyword in C# (which maps to initonly in MSIL) to indicate that the value of the variable can only be set in the constructor; after that, it's an error to change it. This is often used when a field helps to determine the identity of a class, and is often set equal to a constructor parameter.

Community
  • 1
  • 1
landoncz
  • 1,997
  • 14
  • 15
  • 9
    For local variables, there is no performance benefit when using `const`. It doesn't give the compiler any additional information. Check [this answer](https://stackoverflow.com/a/49224799/24874) for more details. – Drew Noakes Mar 12 '18 at 09:03
  • 1
    At least for mono this is not always true. Verified with mono 5.14.0.177. Local consts will cause, when using consts to calculate some value (f.e. const int a = 10; const int b = a + 20;) different statements. ldc.r4 110 instead of ldc.i4.s and other operations to actually calculate the values. (tested with Debug as target, still find it relevant, especially for high-performance code; for low performance the difference might not be so relevant) – Daniel Bişar Sep 07 '18 at 14:26
  • 2
    @SACO if you're worried about perf you should be checking release builds. Even if the Mono compiler emits the addition operation, the JIT would likely apply a basic constant fold to the machine code. Roslyn in C# 7.3 [does the right thing](https://sharplab.io/#v2:EYLgZgpghgLgrgJwgZwLQFEAeAHJzkCWA9gHYBqUCBUwANigDQwgG0A+AAgMwAEHAjADY+AJh4BhALAAoAN4yeivrwHCCJGBNLIYACgCUCpfOlKzPAMbbN6mzwC8PfjwDUPEQG5zR83wDsPAQePjwAviEh3HxCgRo8AHKk4tYGISa+iraBDk6u7sGmGRwBQSHh0qFAA=). – Drew Noakes Mar 14 '19 at 13:39
  • @Drew Noakes nice! And a great link you shared. – Daniel Bişar Mar 16 '19 at 09:47
  • @SACO be sure to check out the [JIT Asm view](https://sharplab.io/#v2:EYLgZgpghgLgrgJwgZwLQFEAeAHJzkCWA9gHYBqUCBUwANigDQwhTIC2APgAIDMABFwCMANgEAmPgGEAsACgA3nL7KB/IaIIkYU0shgAKAJRKVi2Sot8Axru2a7fALx9BfANR8xAbksnLAgHY+Ai8/PgBfMLDeARFgrT4AOVJJWyMws39le2CnF3dPUPMsriCQsMjZcKA===) too. – Drew Noakes Mar 16 '19 at 14:02
46

tl;dr for local variables with literal values, const makes no difference at all.


Your distinction of "inside methods" is very important. Let's look at it, then compare it with const fields.

Const local variables

The only benefit of a const local variable is that the value cannot be reassigned.

However const is limited to primitive types (int, double, ...) and string, which limits its applicability.

Digression: There are proposals for the C# compiler to allow a more general concept of 'readonly' locals (here) which would extend this benefit to other scenarios. They will probably not be thought of as const though, and would likely have a different keyword for such declarations (i.e. let or readonly var or something like that).

Consider these two methods:

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

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

Built in Release mode we see the following (abridged) IL:

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

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

As you can see, they both produce the exact same IL. Whether the local s is const or not has no impact.

The same is true for primitive types. Here's an example using int:

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

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

And again, the 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     
}

So again we see no difference. There cannot be a performance or memory difference here. The only difference is that the developer cannot re-assign the symbol.

Const fields

Comparing a const field with a variable field is different. A non-const field must be read at runtime. So you end up with IL like this:

// Load a const field
ldc.i4       1234

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

It's clear to see how this could result in a performance difference, assuming the JIT cannot inline a constant value itself.

Another important difference here is for public const fields that are shared across assemblies. If one assembly exposes a const field, and another uses it, then the actual value of that field is copied at compile time. This means that if the assembly containing the const field is updated but the using assembly is not re-compiled, then the old (and possibly incorrect) value will be used.

Const expressions

Consider these two declarations:

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

For the const form, the addition must be computed at compile time, meaning the number 3 is kept in the IL.

For the non-const form, the compiler is free to emit the addition operation in the IL, though the JIT would almost certainly apply a basic constant folding optimisation so the generated machine code would be identical.

The C# 7.3 compiler emits the ldc.i4.3 opcode for both of the above expressions.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • Can't agree with the your statement that "for local variables with literal values, const makes no difference at all." They make sense if used in a method with early returns and conditional statements. For example if you declare a variable in the beginning of your method and use it only within if(), then const won't take any byte from the memory until you enter the if statement (while regular variable will eat some bytes anyway). – Yury Kozlov Aug 26 '18 at 02:59
  • 1
    Supposing you were micro-optimising, would it make sense to define constants used in local methods as fields within the class and then reference them within methods? I guess this would be at the expense of readability and also making those methods less "self-contained" (encapsulated). – Dan Diplo Mar 14 '19 at 09:20
  • 1
    @YuryKozlov there is no difference at all. Take a look at the IL. – Drew Noakes Mar 14 '19 at 13:28
  • @DanDiplo if its const it doesn't matter where you define it. The const value ends up being expressed in the IL directly. – Drew Noakes Mar 14 '19 at 13:29
  • @DrewNoakes Why are local variables literals? Aren't they stored on the stack? (If so, there is a difference in preformance and memory no?) – Maarten Vissers Sep 28 '22 at 11:27
  • @MaartenVissers a literal is just a value that is known to the compiler, rather than something that is produced at runtime. Because the compiler knows what the value will be, it can "hard code" the value directly into the compilation's output, which allows it to be a `const`. – Drew Noakes Oct 10 '22 at 03:08
  • @DrewNoakes I see, thanks. Additional question: why are local variables literals and private fields aren't? Even if you assign them just a primitive value... – Maarten Vissers Oct 11 '22 at 09:47
  • @MaartenVissers in the expression `int i = 1`, only the `1` is a literal. So neither a local or field are literals. They may, however, be assigned literal values. A non-literal value would be something like `int i = j`, where `j` refers to some other value, knowable only at runtime. Other examples of literals are `"hello"`, `true` and `1.234`. – Drew Noakes Oct 12 '22 at 00:08
  • @DrewNoakes Ok, and is there a reason why a non-const field must be read at runtime? (It's strange to me, because a non-const variable (in a method) must not) – Maarten Vissers Oct 20 '22 at 11:13
  • The state of a local variable can be reasoned about more effectively than the state of a field. Fields are subject to mutation by other methods, or even the same method called on another thread. Local variables love on the stack (although capturing can hoist to the heap, but still in a controlled manner). – Drew Noakes Oct 20 '22 at 13:15
16

As per my understanding Const values do not exist at run time - i.e. in form of a variable stored in some memory location - they are embeded in MSIL code at compile time . And hence would have an impact on performance. More over run-time would not be required to perform any house keeping (conversion checks / garbage collection etc) on them as well, where as variables require these checks.

YetAnotherUser
  • 9,156
  • 3
  • 39
  • 53
  • 3
    plus 1 for pointing out conversion checks / garbage collection. – Mark Jul 27 '15 at 05:21
  • For the code given in the original question there is no difference to the generated IL, and therefore no difference to memory, checking, garbage collection, etc. – Drew Noakes Mar 11 '18 at 20:39
  • *Const values do not exist at run time* is a bit misleading. They do exist, or else you wouldn't be able to use them. – Liam Jan 08 '19 at 17:10
  • @Liam - agree, I tried to explain it in next sentence. "Const values do not exist at run time - **i.e. in form of a variable stored in some memory location**" – YetAnotherUser Jan 15 '19 at 23:26
4

const is a compile time constant - that means all your code that is using the const variable is compiled to contain the constant expression the const variable contains - the emitted IL will contain that constant value itself.

This means the memory footprint is smaller for your method because the constant does not require any memory to be allocated at runtime.

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • 1
    I don't believe the statement about memory reduction here to be true. `const` only works with primitives (which result in IL that loads constants onto the evaluation stack, as you say) and strings (which allocate the same amount of memory regardless of whether they're const or not). – Drew Noakes Mar 11 '18 at 20:12
  • 1
    Ran some tests and [there is no difference at runtime](https://stackoverflow.com/a/49224799/24874) to memory or anything else. The generated code is identical. – Drew Noakes Mar 11 '18 at 20:37
4

Besides the small performance improvement, when you declare a constant you are explicitly enforcing two rules on yourself and other developers who will use your code

  1. I have to initialize it with a value right now i can't to do it any place else.
  2. I cannot change its value anywhere.

In code its all about readability and communication.

Shrage Smilowitz
  • 24,494
  • 2
  • 28
  • 32
2

A const value is also 'shared' between all instances of an object. It could result in lower memory usage as well.

As an example:

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;
}

Memory consumption is tricky in .Net and I won't pretend to understand the finer details of it, but if you instantiate a list with a million 'Static' it is likely to use considerably less memory than if you do not.

    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();
    }

When using 'NonStatic' the working set is 69,398,528 compared to only 32,423,936 when using static.

Rob P.
  • 14,921
  • 14
  • 73
  • 109
  • The only reference type you can use with `const` is `string`. Both a const string (`const string s = "foo";`) and a string literal (`var s = "foo";`) will be interned. So there's no difference to memory consumption here. All other const values are passed by value. – Drew Noakes Mar 11 '18 at 15:21
  • @DrewNoakes - I'm not sure I fully understand, but I've expanded my answer. Please let me know if I've made a mistake or am missing your point. – Rob P. Mar 11 '18 at 16:35
  • Static is different from const, and has nothing to do with the original question, sorry. I think your edit didn't help your answer. You can't really achieve a test over N elements with const because you end up with a single instance in memory, just as you would with a compile time string. For example, a string literal `"hello"` will be interned, so two methods that define `"hello"` as local variables will end up sharing the same reference (on the basis that strings are immutable). However `"hello".ToUpper()` is a runtime value, so multiple calls will produce multiple instances. – Drew Noakes Mar 11 '18 at 20:04
  • I provided more info in [this answer](https://stackoverflow.com/a/49224799/24874). – Drew Noakes Mar 11 '18 at 20:36
1

The const keyword tells the compiler that it can be fully evaluated at compile time. There is a performance & memory advantage to this, but it is small.

Chris Trombley
  • 2,232
  • 1
  • 17
  • 24
  • 1
    Actually, there is no performance or memory difference in the case of a local variable. See [my answer](https://stackoverflow.com/a/49224799/24874). – Drew Noakes Mar 11 '18 at 20:36
1

Constants in C# provide a named location in memory to store a data value. It means that the value of the variable will be known in compile time and will be stored in a single place.

When you declare it, it is kind of 'hardcoded' in the Microsoft Intermediate Language (MSIL).

Although a little, it can improve the performance of your code. If I'm declaring a variable, and I can make it a const, I always do it. Not only because it can improve performance, but also because that's the idea of constants. Otherwise, why do they exist?

Reflector can be really useful in situations like this one. Try declaring a variable and then make it a constant, and see what code is generated in IL. Then all you need to do is see the difference in the instructions, and see what those instructions mean.

Oscar Mederos
  • 29,016
  • 22
  • 84
  • 124
  • Isn't your second sentence exactly the opposite of what @BrokenGlass wrote in his answer? – M4N Aug 29 '11 at 06:38