C# 什么';为什么不能在开关块中使用只读字段?
我很清楚,C#不允许C# 什么';为什么不能在开关块中使用只读字段?,c#,switch-statement,runtime,constants,language-lawyer,C#,Switch Statement,Runtime,Constants,Language Lawyer,我很清楚,C#不允许switch块中的readonly字段,而这正是地址所在 我想知道为什么会这样。这仅仅是一个武断的语言规范怪癖,还是背后有一个技术原因?如果是,那是什么技术原因 让我明确一点,我理解const和readonly之间的区别,并且我知道C#开关需要const值,或者编译时已知的值。 对我来说,从功能上讲,使用一堆if..else if语句与使用switch语句具有相同的结果,因为无论我用switch语句做什么,我都可以用if语句实现,例如: const int MyConstan
switch
块中的readonly
字段,而这正是地址所在
我想知道为什么会这样。这仅仅是一个武断的语言规范怪癖,还是背后有一个技术原因?如果是,那是什么技术原因
让我明确一点,我理解const
和readonly
之间的区别,并且我知道C#开关
需要const
值,或者编译时已知的值。
对我来说,从功能上讲,使用一堆if..else if
语句与使用switch
语句具有相同的结果,因为无论我用switch
语句做什么,我都可以用if
语句实现,例如:
const int MyConstantValue = 10;
int myCompareValue = 3;
if(myCompareValue == MyConstantValue)
{
//...
}
else
{
//...
}
switch(myCompareValue)
{
case MyConstantValue:
//...
break;
default:
//...
break;
}
这两种构造都有相同的结果:执行else
或default
块,但是if
可以在没有编译时常量或已知值的情况下执行。如果开关不能这样做,那么为什么可以这样做呢?在文档中,只读字段是一个运行时常量。(与常量字段相反)
同时,switch语句期望在编译时得到一个初始化值
因此,编译器拒绝只读字段
Edit:不是C#编译器专家,但它似乎在IL中创建了一个分支表,而不是if语句(在某些情况下)。有关更多信息,请查看
引用相关部分:
…首次将函数称为字典时
将创建字符串(key)和int(value)的集合,并且所有的大小写都是
存储在此字典中作为键和整数作为值
靠着它储存。然后,对于switch语句,取字符串
并在字典中查询,如果存在,则为数值
因为返回了字符串。编译器使用这个数字创建一个
高效的跳转表,它跳转到目标控制台
绳子
现在答案是:)。字符串预先存储在字典中。如果
case语句中的字符串不是常量,而是其中的更改
不会反映在字典中,因此你会比较
反对过时的价值观。为了避免这种不一致性,非常量
根本不支持值
显然,对于动态值,不能使用字典,因此
开关箱不可能进行优化,因此无论如何都应该进行优化
使用if-then-else
这是因为只读变量不是常量。它们可以在声明或类构造函数中赋值。因此,它的值在设计时是未知的。想想编译器如何处理开关
块:它基本上是基于编译时已知的相关表达式的目标值构建一系列条件分支
现在假设你有这个:
private readonly int N = System.DateTime.Ticks;
如果尝试使用N作为开关
块的目标值,则编译时该值未知。那么编译器应该做什么呢?尽管它当然可以构建一系列更复杂的条件分支,但这种模式的“效用”真的能证明这一点吗
就我个人而言,我更希望编译器编写人员把时间花在其他事情上……开关盒标签只能是编译时常量(至少在C#7发布之前,开关对模式匹配也很有用)。readonly
字段不是编译时常量,而是在执行代码时初始化的字段
只读
字段与常规字段之间的唯一区别在于前者只能在构造函数中或通过字段初始值设定项进行初始化,而后者可以在任何地方进行初始化/重新分配
在一个只读字段或常量之间看到这个差值,只需考虑下面的代码:
bool const True = true;
if (!true)
throw new Exception(); //Compiler warning: unreachable code detected
return;
这里,编译器知道编译时的True
是什么,因此可以推断throw语句的可达性,这显然是不可达的if(false)
将始终忽略以下块/语句
现在考虑以下内容:
readonly static bool True = true;
if (!true)
throw new Exception();
return;
这将很好地编译。在包含字段的类初始化之前,编译器无法知道True
实际上是True
。这意味着代码必须运行,只有运行时才能推断出True
的真正含义
至于为什么switch
case标签需要保持不变,我相信这主要是由于优化原因(更高效的代码)和覆盖率分析;编译器可以推断出是否处理了所有可能的情况,这在许多情况下非常有用,但对于非常量标签来说是不可能的
如果您需要基于非常量表达式进行分支,那么只需使用If
else If
else
,原因是C开关是在具有相同约束的C/C++开关之后建模的
这种限制有两个原因:
- 性能:switch语句可以编译成一个非常有效的“跳转表”,如果在编译时不知道这些情况,这是不可能的李>
- 正确性:switch语句在编译时具有可证明的唯一事例,但没有在编译时无法证明的常量事例李>
这并不能回答为什么switch语句需要编译时常量。这个答案正是我在问题中链接的问题中所讨论的。为了澄清:readonly
变量是从构造函数中设置的<代码>常量
变量是在编译时设置的。@Logan这不是你的问题。@PatrickHofman这是我的问题<代码>我想了解