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

M4N*_*M4N 70 c# resharper coding-style 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;
Run Code Online (Sandbox Code Playgroud)

鉴于这些是真正的常量值(而不是变量)我理解ReSharper建议将它们更改为const.

但除此之外,使用const(例如更好的性能)是否有任何其他优势可以证明使用const BindingFlags而不是方便可读的var关键字?

BTW:我刚刚在这里发现了一个类似的问题:Resharper总是建议我创建const字符串而不是字符串,但我认为它更多的是关于类的字段,我的问题是关于局部变量/ consts.

lan*_*ncz 82

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

此外,通常使用常量与变量有一个小的性能优势.根据MSDN杂志问答,这与编译到MSIL的方式有关:

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

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

  • 对于局部变量,使用`const`时没有性能优势.它不会为编译器提供任何其他信息.查看[此答案](/sf/answers/3445735961/)了解更多详情. (6认同)
  • @SACO如果您担心性能问题,应该检查发布版本。即使Mono编译器发出加法运算,JIT也可能会将基本常量折叠应用于机器代码。罗斯林在C#7.3做正确的事(https://sharplab.io/#v2:EYLgZgpghgLgrgJwgZwLQFEAeAHJzkCWA9gHYBqUCBUwANigDQwgG0A+AAgMwAEHAjADY+AJh4BhALAAoAN4yeivrwHCCJGBNLIYACgCUCpfOlKzPAMbbN6mzwC8PfjwDUPEQG5zR83wDsPAQePjwAviEh3HxCgRo8AHKk4tYGISa+iraBDk6u7sGmGRwBQSHh0qFAA=)。 (2认同)

Dre*_*kes 23

tl; dr用于具有文字值的局部变量,const根本没有任何区别.


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

Const局部变量

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

但是const仅限于原始类型(int,, double......)string,这限制了它的适用性.

题外话:C#编译器有一些建议允许更一般的'readonly'本地化概念(这里),这会将这种好处扩展到其他场景.他们可能不会像想到的const,虽然,很可能有这种声明(即不同的关键字letreadonly var或类似的东西).

考虑这两种方法:

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

private static string LocalConstString()
{
    const string s = "hello";
    return s;
}
Run Code Online (Sandbox Code Playgroud)

内置Release模式我们看到以下(删节)IL:

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

.method private hidebysig static string LocalConstString() cil managed 
{
    ldstr        "hello"
    ret          
}
Run Code Online (Sandbox Code Playgroud)

如您所见,它们都产生完全相同的IL.无论当地s是否const有影响.

原始类型也是如此.这是一个使用示例int:

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

private static int LocalConstInt()
{
    const int i = 1234;
    return i;
}
Run Code Online (Sandbox Code Playgroud)

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     
}
Run Code Online (Sandbox Code Playgroud)

所以我们再次看到没有区别.这里不存在性能或内存差异.唯一的区别是开发人员无法重新分配符号.

Const字段

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

// Load a const field
ldc.i4       1234

// Load a non-const field
ldsfld       int32 MyProject.MyClass::_myInt
Run Code Online (Sandbox Code Playgroud)

很明显,假设JIT本身不能内联一个常量值,这会导致性能差异.

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


Yet*_*ser 14

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

  • 加1表示转换检查/垃圾收集. (2认同)