在方法中使用const而不是变量的优点

时间:2011-04-29 15:17:53

标签: c# coding-style resharper 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 always suggesting me to make const string instead of string,但我认为这更多是关于一个类的字段,我的问题是关于局部变量/ consts。

8 个答案:

答案 0 :(得分:81)

如果您尝试将值分配给常量,编译器将抛出错误,从而可能会阻止您意外更改它。

此外,通常使用常量与变量有一个小的性能优势。这与它们编译为MSIL的方式有关,符合this MSDN magazine Q&A

  

现在,无论在代码中引用myInt,而不是必须执行“ldloc.0”来从变量中获取值,MSIL只需加载硬编码到MSIL中的常量值。 因此,使用常量通常具有较小的性能和内存优势。但是,为了使用它们,您必须在编译时具有变量的值,并且在编译时对该常量的任何引用即使他们在不同的集会中,也会进行这种替换。

     

如果在编译时知道值,常量肯定是一个有用的工具。如果不这样做,但想确保您的变量只设置一次,则可以使用C#中的readonly关键字(映射到MSIL中的initonly)来指示变量的值只能在构造函数中设置;在那之后,改变它是一个错误。这通常在字段有助于确定类的标识时使用,并且通常设置为等于构造函数参数。

答案 1 :(得分:18)

对于具有文字值的局部变量,

tl; dr const完全没有区别。

你对"内部方法的区分"非常重要。让我们看一下,然后将其与const字段进行比较。

Const局部变量

const局部变量的唯一好处是无法重新分配该值。

const仅限于基本类型(intdouble,...)和string,这限制了它的适用性。

题外话:有人建议C#编译器允许更一般的概念' readonly'本地人(here)会将此优势扩展到其他方案。它们可能不会被视为const,并且可能会为此类声明设置不同的关键字(例如letreadonly 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字段

const字段与变量字段进行比较不同。必须在运行时读取非const字段 。所以你最终得到这样的IL:

// Load a const field
ldc.i4       1234

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

很明显,如果JIT无法内联一个恒定值,这会导致性能差异。

此处的另一个重要区别是跨程序集共享的公共const字段。如果一个程序集公开const字段,而另一个程序集使用它,那么该字段的实际值将在编译时复制。这意味着如果更新包含const字段的程序集但不重新编译using程序集,则将使用旧的(可能不正确的)值。

Const表达式

考虑这两个声明:

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

对于const形式,必须在编译时计算加法,这意味着数字3保存在IL中。

对于非const形式,编译器可以自由地在IL中发出加法运算,尽管JIT几乎肯定会应用基本的常量折叠优化,因此生成的机器代码将是相同的。

上述两种表达方式的C# 7.3 compiler emits the ldc.i4.3 opcode

答案 2 :(得分:14)

根据我的理解,Const值在运行时不存在 - 即以存储在某些存储器位置的变量的形式 - 它们在编译时嵌入在MSIL代码中。因此会对性能产生影响。在变量需要这些检查时,不需要更多的运行时间来执行任何内容保存(转换检查/垃圾回收等)。

答案 3 :(得分:4)

const是一个编译时常量 - 这意味着所有使用const变量的代码都被编译为包含const变量包含的常量表达式 - 发出的IL将包含该常量值本身。

这意味着您的方法的内存占用量较小,因为该常量不需要在运行时分配任何内存。

答案 4 :(得分:3)

除了小的性能改进之外,当您声明一个常量时,您明确地对自己和将使用您的代码的其他开发人员实施两个规则

  1. 我现在必须用一个值初始化它,我不能在任何其他地方做它。
  2. 我不能在任何地方改变它的价值。
  3. 代码中的所有关于可读性和通信的内容。

答案 5 :(得分: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();
    }

使用'NonStatic'时,工作集为69,398,528,而使用静态时仅为32,423,936。

答案 6 :(得分:1)

const关键字告诉编译器它可以在编译时完全评估。有一个表演&amp;记忆优势,但它很小。

答案 7 :(得分:1)

C#中的常量提供命名的内存位置来存储数据值。这意味着变量的值将在编译时知道,并将存储在一个地方。

当您声明它时,它在Microsoft中间语言(MSIL)中是一种“硬编码”。

虽然有点,但它可以提高代码的性能。如果我声明一个变量,我可以使它成为一个常量,我总是这样做。不仅因为它可以提高性能,还因为它是常量的概念。否则,它们为什么存在?

反射器在这样的情况下非常有用。尝试声明变量然后使其成为常量,并查看 IL 中生成的代码。然后,您需要做的就是查看说明中的差异,并查看这些说明的含义。