每当我在方法中有局部变量时,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。
如果您尝试将值分配给常量,编译器将抛出错误,从而可能会阻止您意外更改它。
此外,通常使用常量与变量有一个小的性能优势。这与它们编译到MSIL的方式有关,每个this MSDN magazine Q&A:
现在,无论在代码中引用myInt,而不是必须执行“ldloc.0”来从变量中获取值,MSIL只需加载硬编码到MSIL中的常量值。因此,使用常量通常具有较小的性能和内存优势。但是,为了使用它们,您必须在编译时具有变量的值,并且在编译时对该常量的任何引用,即使它们位于不同的程序集中,也会进行此替换。
如果在编译时知道值,常量肯定是一个有用的工具。如果不这样做,但想确保您的变量只设置一次,则可以使用C#中的readonly关键字(映射到MSIL中的initonly)来指示变量的值只能在构造函数中设置;在那之后,改变它是一个错误。这通常在字段有助于确定类的标识时使用,并且通常设置为等于构造函数参数。
tl; dr用于具有文字值的局部变量,const
完全没有区别。
你对“内部方法”的区分非常重要。让我们看看它,然后将它与const
字段进行比较。
const
局部变量的唯一好处是无法重新分配该值。
然而,const
仅限于原始类型(int
,double
,...)和string
,这限制了它的适用性。
题外话:有人建议C#编译器允许更一般的'readonly'本地化概念(here),这会将这种好处扩展到其他场景。它们可能不会被认为是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;
}
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字段。所以你最终得到这样的IL:
// Load a const field
ldc.i4 1234
// Load a non-const field
ldsfld int32 MyProject.MyClass::_myInt
很明显,假设JIT本身不能内联一个常量值,这会导致性能差异。
这里的另一个重要区别是跨程序集共享的公共const字段。如果一个程序集公开const字段,而另一个程序集使用它,则在编译时复制该字段的实际值。这意味着如果更新包含const字段的程序集但不重新编译using程序集,则将使用旧的(可能不正确的)值。
考虑这两个声明:
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用于上述两种表达方式。
根据我的理解,Const值在运行时不存在 - 即以存储在某些存储器位置的变量的形式 - 它们在编译时嵌入在MSIL代码中。因此会对性能产生影响。在变量需要这些检查的情况下,不需要更多的运行时间来执行任何内容保存(转换检查/垃圾收集等)。
const是一个编译时常量 - 这意味着所有使用const变量的代码都被编译为包含const变量包含的常量表达式 - 发出的IL将包含该常量值本身。
这意味着您的方法的内存占用量较小,因为该常量不需要在运行时分配任何内存。
除了小的性能改进之外,当您声明一个常量时,您明确地对自己和将使用您的代码的其他开发人员实施两个规则
代码中的所有关于可读性和通信。
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。
const关键字告诉编译器它可以在编译时完全评估。这有一个性能和内存优势,但它很小。
C#中的常量在内存中提供一个命名位置来存储数据值。这意味着变量的值将在编译时知道,并将存储在一个地方。
当您声明它时,它在Microsoft中间语言(MSIL)中是一种“硬编码”。
虽然有点,但它可以提高代码的性能。如果我声明一个变量,我可以使它成为一个常量,我总是这样做。不仅因为它可以提高性能,还因为它是常量的概念。否则,它们为什么存在?
反射器在这样的情况下非常有用。尝试声明变量然后使其成为常量,并查看IL中生成的代码。然后,您需要做的就是查看说明中的差异,并查看这些说明的含义。