Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 显式局部作用域-有什么真正的好处吗?_C#_Scope - Fatal编程技术网

C# 显式局部作用域-有什么真正的好处吗?

C# 显式局部作用域-有什么真正的好处吗?,c#,scope,C#,Scope,我正在清理一些代码,并删除了不再需要的if语句。然而,我意识到我忘了取下支架。这当然是有效的,只是创建了一个新的局部范围。这让我开始思考。在我多年的C#开发过程中,我从未找到使用它们的理由。事实上,我有点忘了我能做到 定义局部范围有什么实际好处吗?我知道我可以在一个范围内定义变量,然后在一个不相关的范围内(for、foreach等)再次定义相同的变量,如下所示: void SomeMethod() { { int i = 20; } int i

我正在清理一些代码,并删除了不再需要的
if
语句。然而,我意识到我忘了取下支架。这当然是有效的,只是创建了一个新的局部范围。这让我开始思考。在我多年的C#开发过程中,我从未找到使用它们的理由。事实上,我有点忘了我能做到

定义局部范围有什么实际好处吗?我知道我可以在一个范围内定义变量,然后在一个不相关的范围内(for、foreach等)再次定义相同的变量,如下所示:

void SomeMethod()
{    
    {
        int i = 20;
    }

    int i = 50; //Invalid due to i already being used above.
}

void SomeMethod2()
{    
    {
        int i = 20;
    }
    {
        int i = 50; //Valid due to scopes being unrelated.
    }
    {
        string i = "ABCDEF";
    }
}

定义局部范围的真正意义是什么?实际上是否会有任何形式的性能增益(或潜在的损失)?我知道C++可以做到这一点,是帮助你管理内存的一部分,但是因为这是.NET,会不会有好处?这仅仅是让我们定义随机作用域的语言的bi产品,即使没有真正的好处

与函数一样,这些“块”基本上只是隔离函数中(大部分)不相关代码的区域及其局部变量

例如,如果需要一些临时变量在两个函数调用之间传递,可以使用它

int Foo(int a) {

    // ...

    {
        int temp;
        SomeFuncWithOutParam(a, out temp);

        NowUseThatTempJustOnce(temp);            
    }

    MistakenlyTryToUse(temp);    // Doesn't compile!

    // ...

}
然而,有人可能会断言,如果您需要这种词法范围,内部块无论如何都应该是独立的函数


至于表现等等,我非常怀疑这是否重要。编译器将函数视为一个整体,并在确定堆栈帧大小时收集所有局部变量(甚至是内联声明的变量)。所以基本上所有的局部变量都集中在一起了。对变量的使用给予更多的限制纯粹是一种词法上的东西。

局部作用域可能有用的地方:在
开关的
语句中的
case
语句中。默认情况下,所有案例都与
开关
语句共享相同的范围。 不允许在多个
case
语句中声明具有相同名称的局部临时变量,最终可能只在第一个case语句中声明变量,甚至在
switch
-语句之外声明变量。 您可以通过为每个case语句指定一个局部范围并在该范围内声明临时变量来解决此问题。
但是,不要使您的案例太复杂,这个问题可能表明最好调用单独的方法来处理
案例
-语句。

优点主要是它使语言的定义更简单

这个定义现在可以简单地说,如果,while,for,等等。。后面应该有一个单独的语句。括号内的一组语句只是另一种可能的语句

禁止您的示例中使用的语句块并没有真正的好处。有时,它们有助于避免名称冲突,但您可以在没有冲突的情况下解决问题。它也将毫无意义地引入语法规则的差异,与C语言、C++语言和java语言相比。 如果您想知道它也不会改变被引用对象的对象生命周期。

在C#中,将一组语句转换为一个语句纯粹是语法。对于任何需要一条语句的关键字都是必需的,例如if、for、using等。一些特殊情况:

  • 开关中的case关键字是特殊的,因为它不要求它是单个语句。break或goto关键字结束它。这解释了为什么可以使用大括号插入变量声明
  • try和catch关键字很特殊,即使后面只有一条语句,它们也需要大括号。这很不寻常,但可能是受程序员思考块内声明范围的启发,catch块不能引用try块内的变量,因为异常处理的工作方式
用它限制局部变量的范围是一个失败的原因。在C++中,这是一个很大的问题,因为结束括号是编译器将在范围块内为变量注入析构函数调用的地方。这是ab/用于RAII模式的所有时间,在程序中使用标点符号并不会产生如此剧烈的副作用

C#团队对此没有太多选择,局部变量的生存时间严格受抖动控制。它对方法中的任何分组构造都不感兴趣,它只知道IL。除了try/except/finally之外,它没有任何分组构造。任何局部变量的范围,无论它写在哪里,都是方法的主体。当在编译的C#代码上运行ildasm.exe时,您会看到局部变量被提升到方法体的顶部。这也部分解释了为什么C#编译器不允许您在另一个作用域块中声明另一个同名的局部变量

抖动有关于局部变量生存期的有趣规则,它们完全由垃圾收集器的工作方式决定。当它JIT一个方法时,它不仅为该方法生成机器代码,而且还创建一个表来描述每个局部变量的实际范围、初始化它的代码地址和不再使用它的代码地址。垃圾收集器使用该表根据活动执行地址来决定对对象的引用是否有效


这使得它在收集对象时非常有效。当您与本机代码进行互操作时,有时效率有点太高而且很麻烦,您可能需要神奇的GC.KeepAlive()方法来延长生命周期。一个非常出色的方法,它根本不生成任何代码。它唯一的用途是获得抖动来更改表,并为变量生命周期插入更大的地址。

至少在释放模式下不会有性能提升:GC可以收集对象,如果它知道-或者至少