【问题标题】:What is the value of an anonymous unattached block in C#?C# 中匿名未附加块的值是什么?
【发布时间】:2008-09-17 16:54:04
【问题描述】:

在 C# 中,您可以在不附加到任何其他语句的方法内部创建一个块。

    public void TestMethod()
    {
        {
            string x = "test";
            string y = x;

            {
                int z = 42;
                int zz = z;
            }
        }
    }

这段代码编译和运行就像 main 方法中的大括号不存在一样。还要注意块内的块。

是否存在这样有价值的场景?我还没有发现,但很想知道其他人的发现。

【问题讨论】:

    标签: c#


    【解决方案1】:

    范围和垃圾回收:当您离开未附加的块时,其中声明的任何变量都会超出范围。这让垃圾收集器可以清理这些对象。

    Ray Hayes 指出 .NET 垃圾收集器不会立即收集超出范围的对象,因此作用域是主要的好处。

    【讨论】:

    • 您是否真的在 VM 级别验证了这一点?大多数合理的编译器和 GC 会倾向于在编译过程中将两个块合并为一个,并且可以在最后一次使用引用它的变量后 GC 一个值。
    • 我没有验证垃圾回收,但我已经验证了范围。我很确定 .NET 垃圾收集器会收集已离开范围/未被引用的东西。
    • GC 可以启动,但可能暂时不会!范围的丢失并不意味着自动 GC。此外,在这种情况下,对于值类型,GC 将没有任何东西。作用域是这里的主要目的,而不是 GC。
    【解决方案2】:

    一个例子是如果你想重用一个变量名,通常你不能重用变量名 这是无效的

            int a = 10;
            Console.WriteLine(a);
    
            int a = 20;
            Console.WriteLine(a);
    

    但这是:

        {
            int a = 10;
            Console.WriteLine(a);
        }
        {
            int a = 20;
            Console.WriteLine(a);
        }
    

    我现在唯一能想到的,例如,如果你正在处理一个大对象,你从中提取了一些信息,然后你要执行一堆操作,你可以把块中处理大对象,使其超出范围,然后继续其他操作

        {
            //Process a large object and extract some data
        }
        //large object is out of scope here and will be garbage collected, 
        //you can now perform other operations with the extracted data that can take a long time, 
        //without holding the large object in memory
    
        //do processing with extracted data
    

    【讨论】:

      【解决方案3】:

      语句是简单语句或块的解析器规则的副产品。即,可以在任何可以使用单个语句的地方使用块。

      例如

      if (someCondition)
       SimpleStatement();
      
      if (SomeCondition)
      {
         BlockOfStatements();
      }
      

      其他人指出,变量声明在包含块末尾之前都在范围内。临时变量的范围很短是件好事,但我从来不需要单独使用块来限制变量的范围。有时你会在“使用”语句下面使用一个块。

      所以一般来说它不值钱。

      【讨论】:

        【解决方案4】:

        据我所知,它仅从组织的角度有用。我真的无法想象这样做有什么合乎逻辑的价值。也许有人会有一个合适的例子。

        【讨论】:

          【解决方案5】:

          这允许您在任何地方创建范围块。它本身并没有那么有用,但可以使逻辑更简单:

          switch( value )
          {
              case const1: 
                  int i = GetValueSomeHow();
                  //do something
                  return i.ToString();
          
              case const2:
                  int i = GetADifferentValue();
                  //this will throw an exception - i is already declared
           ...
          

          在 C# 中,我们可以使用范围块,以便在每种情况下声明的项目仅在这种情况下处于范围内:

          switch( value )
          {
              case const1: 
              {
                  int i = GetValueSomeHow();
                  //do something
                  return i.ToString();
              }
          
              case const2:
              {
                  int i = GetADifferentValue();
                  //no exception now
                  return SomeFunctionOfInt( i );
              }
           ...
          

          这也适用于 goto 和标签,而不是你经常在 C# 中使用它们。

          【讨论】:

            【解决方案6】:

            它存在的一个实际原因是,如果您想限制某个变量的范围,而无需为该块引入任何其他原因。在实际实践中,这几乎没有用处。

            就我个人而言,我的猜测是,从语言/编译器的角度来看,更容易说您可以将块放置在预期语句的任何位置,而且它们并没有竭尽全力阻止您使用它没有 if/for/method 声明/等。

            考虑一下 Eric Lippert 的开头 this recent blog postif 语句后面没有单个语句或用大括号括起来的多个语句,它只是后跟一个语句。任何时候你将 0 到 N 语句括在花括号中,你就使那段代码等效(从语言解析器的角度来看)一个语句。同样的做法也适用于所有循环结构,尽管正如博文的主要观点所解释的那样,它不适用于 try/catch/finally 块。

            从这个角度处理块时,问题就变成了:“是否有令人信服的理由阻止在任何可以使用单个语句的地方使用块?”答案是“不”。

            【讨论】:

              【解决方案7】:

              这样做的一个原因是变量“z”和“zz”不能用于该内部块末尾以下的代码。当您在 Java 中执行此操作时,JVM 会为内部代码推送一个堆栈帧,这些值可以存在于堆栈中。当代码退出块时,堆栈帧被弹出并且这些值消失。根据所涉及的类型,这可以使您不必使用堆和/或垃圾收集。

              【讨论】:

                【解决方案8】:

                在 C# 中——如 c/c++/java——大括号表示范围。这决定了变量的生命周期。当到达右大括号时,该变量立即可用于垃圾回收。在 c++ 中,如果 var 表示一个实例,它将导致调用类的析构函数。

                至于用法,唯一可能的用途是释放一个大对象,但tbh,将其设置为 null 将具有相同的效果。我怀疑前一种用法可能只是为了让 c++ 程序员在某种程度上在熟悉和舒适的领域转向托管代码。如果真的想在 c# 中调用“析构函数”,通常需要实现 IDisposable 接口并使用“using (var) {...}”模式。

                欧辛

                【讨论】:

                  【解决方案9】:

                  即使出于任何原因(例如变量范围控制)它实际上很有用,但从良好的旧代码可读性的角度来看,我不鼓励您使用这种结构。

                  【讨论】:

                    【解决方案10】:

                    除了语义、范围和垃圾收集之外,这没有任何价值,在这个有限的例子中,这些都不重要。如果您认为它使代码更清晰,对您自己和/或其他人来说,那么您当然可以使用它。然而,代码中更被接受的语义澄清约定通常只使用带有选项 in-line cmets 的换行符:

                    public void TestMethod()
                    {
                        //do something with some strings
                        string x = "test";
                        string y = x;
                    
                        //do something else with some ints
                        int z = 42;
                        int zz = z;
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2010-10-04
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-08-11
                      • 1970-01-01
                      • 2011-07-02
                      • 1970-01-01
                      • 2012-02-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多