当前位置: 首页 > 知识库问答 >
问题:

什么时候在C#中使用没有语句的scope?

锺离浩慨
2023-03-14

最近我发现你可以用C#:

{
    // google
    string url = "#";

    if ( value > 5 )
        url = "http://google.com";

    menu.Add( new MenuItem(url) );
}
{
    // cheese
    string url = "#"; // url has to be redefined again, 
                      // so it can't accidently leak into the new menu item

    if ( value > 45 )
        url = "http://cheese.com";

    menu.Add( new MenuItem(url) );
}

而不是即:

    string url = "#";

    // google
    if ( value > 5 )
        url = "http://google.com";

    menu.Add( new MenuItem(url) );


    // cheese
    url = "#"; // now I need to remember to reset the url

    if ( value > 45 )
        url = "http://cheese.com";

    menu.Add( new MenuItem(url) );

这可能是一个坏例子,可以用很多其他方式来解决。

是否存在“无语句范围”功能是一种良好实践的模式?

共有3个答案

李鹏
2023-03-14

您可以使用{}来重新调整变量名称的用途(即使对于不同的类型):

{
   var foo = new SomeClass();
}
{
   Bar foo = new Bar(); // impairs readability
}

然而,以这种方式重新利用变量会混淆易读性。

因此,在大多数情况下,应该将代码相应地重构为单独的函数,而不是在没有前面语句的情况下使用“未经请求的”范围块。

编辑

在我看来,任何时候需要强制重置局部可变变量值或将其重新用于其他或替代问题都是一种气味的迹象。e、 g.原始代码可以重构为:

menu.Add( value > 5 
            ? new MenuItem("http://google.com")
            : new MenuItem("#"));

menu.Add( value > 45 
            ? new MenuItem("http://cheese.com")
            : new MenuItem("#"));

我相信这传达了我们的意图,没有不应用回退的风险,也不需要显式的局部可变变量来保持状态。

(或新菜单项(值

编辑:范围对寿命没有影响

范围可用于限制昂贵对象寿命的方法

我最初的帖子错误地指出,局部范围可以用来影响对象的寿命。这对于调试和发布版本都是不正确的,无论变量名是否重新分配,正如Jeppe的IL反汇编和此处的这些单元测试所示。感谢Jeppe指出这一点。此外,Lasse指出,即使没有显式超出范围,不再使用的变量也可以在发布版本中进行垃圾收集。

TL;DR尽管使用未经请求的作用域可能有助于将变量作用域的逻辑使用传达给人,但这样做不会影响对象是否符合相同方法中的收集条件。

即在下面的代码中,范围界定,甚至重新使用下面的变量foo对寿命绝对没有影响。

void MyMethod()
{
  // Gratuituous braces:
  {
      var foo = new ExpensiveClass();
  }
  {
      Bar foo = new Bar(); // Don't repurpose, it impairs readability!
  }
  Thread.Sleep(TimeSpan.FromSeconds(10));
  GC.Collect();
  GC.WaitForPendingFinalizers();
  <-- Point "X"
}

在点X:

  • DEBUG构建中,尽管黑客试图诱使GC这样做,但foo变量都不会被收集。
  • RELEASE构建中,无论范围如何,只要不需要,两个fos都有资格进行收集。当然,收集的时间应该留给它自己的设备。
须捷
2023-03-14

如果你问他们为什么要实现这个功能?

我开发了自己的语言,我发现“没有语句的范围”语法很容易就渗透到语法中。如果你愿意的话,这是一个副作用,当你写一个语法很好的时候

在我的语言中,我有同样的功能,我从来没有为它明确地“设计”过——我免费获得它。我从来没有坐在办公桌上,想“哦,有这样的‘特色’难道不酷吗?”。见鬼,起初,我甚至不知道我的语法允许这样做。

“{}”是一个“复合语句”,因为它简化了您想要使用它的所有位置(条件、循环体等)的语法。。。因为这样可以在控制单个语句时省去大括号('if(a

它可以在语句出现的任何地方使用,这一事实与此直接相关;正如其他答案所说,这是无害的,偶尔也有帮助。“如果它没有坏,就不要修理它.-凯什拉姆。

因此,问题不在于“他们为什么要实施它”,而是“他们为什么不禁止它/为什么允许它?”

我可以在我的语言中禁止此特定功能吗?当然可以,但我看不出有什么理由——这将是公司的额外成本。

现在,上面的故事可能适用于C#,也可能不适用于C#,但我没有设计语言(我也不是真正的语言设计师),所以很难说到底为什么,但我想我还是要提一下。

C中也有同样的功能,它实际上有一些用例——如果对象超出范围,它允许确定性地运行对象的终结器,尽管C#的情况并非如此。

也就是说,我在4年的C#(嵌入式语句-

看看C#语法:http://msdn.microsoft.com/en-us/library/aa664812(v=vs.71)。aspx

此外,正如Jeppe所说,我使用了“{}”语法来确保“开关”构造中的每个case块都有单独的本地范围:

int a = 10;
switch(a)
{
    case 1:
    {
        int b = 10;
        break;
    }

    case 2:
    {
        int b = 10;
        break;
    }
}
盛建德
2023-03-14

我发现在许多情况下都可以接受的一个用途是将switch语句的每个switch部分封装在局部范围内。

延迟添加

局部作用域块{…}似乎与生成的IL字节码无关。我尝试了这个简单的例子:

static void A()
{
    {
        var o = new object();
        Console.WriteLine(o);
    }

    var p = new object();
    Console.WriteLine(p);
}

static void B()
{
    var o = new object();
    Console.WriteLine(o);

    var p = new object();
    Console.WriteLine(p);
}


static void C()
{
    {
        var o = new object();
        Console.WriteLine(o);
    }

    {
        var o = new object();
        Console.WriteLine(o);
    }
}

这是在发布模式下编译的(已启用优化)。根据IL DASM得出的IL为:

.method private hidebysig static void  A() cil managed
{
  // Code size       25 (0x19)
  .maxstack  1
  .locals init ([0] object o,
           [1] object p)
  IL_0000:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_000c:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0011:  stloc.1
  IL_0012:  ldloc.1
  IL_0013:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0018:  ret
} // end of method LocalScopeExamples::A
.method private hidebysig static void  B() cil managed
{
  // Code size       25 (0x19)
  .maxstack  1
  .locals init ([0] object o,
           [1] object p)
  IL_0000:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_000c:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0011:  stloc.1
  IL_0012:  ldloc.1
  IL_0013:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0018:  ret
} // end of method LocalScopeExamples::B
.method private hidebysig static void  C() cil managed
{
  // Code size       25 (0x19)
  .maxstack  1
  .locals init ([0] object o,
           [1] object V_1)
  IL_0000:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_000c:  newobj     instance void [mscorlib]System.Object::.ctor()
  IL_0011:  stloc.1
  IL_0012:  ldloc.1
  IL_0013:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0018:  ret
} // end of method LocalScopeExamples::C

结论:

  • 本地范围声明在IL字节码中不存在任何形式。
  • C#编译器将为否则会发生冲突的局部变量选择新名称(C方法中的第二个变量o)。
  • 当他们在C#源代码中引入本地范围时,认为垃圾收集器可能能够更早完成工作的人是错误的。

我还尝试了在调试模式下编译(没有优化)。局部作用域的开始和结束似乎只显示为nop指令(“无操作”)。在某些情况下,来自不同局部作用域的两个同名局部变量映射到IL中的同一个局部变量,就像上面命名为C的C方法一样。只有当两个变量的类型兼容时,才可能实现这两个变量的“统一”。

 类似资料:
  • 问题内容: 我正在重新设计一个使用最小数据库的PHP驱动的网站。原始版本使用“伪准备语句”(执行引号和参数替换的PHP函数)来防止注入攻击并将数据库逻辑与页面逻辑分开。 用一个使用PDO和真正的预备语句的对象替换这些临时功能似乎很自然,但是在对它们进行阅读之后,我不太确定。PDO似乎仍然是一个好主意,但是准备好的语句的主要卖点之一是能够重用它们……我永远不会。这是我的设置: 这些语句都很简单。大多

  • 问题内容: 奇怪的是: 似乎或多或少被定义为。通过这种方式很容易产生错误: 一些fname意外地以else块结尾。修复很简单,我们应该改用它,但是从表面上看,这似乎是一种不错的pythonic方式,并且比“正确”的方式更具可读性。 由于字符串是不可变的,所以为什么字符串错误是什么技术细节?什么时候进行身份检查更好,什么时候进行平等检查更好? 问题答案: 据我所知,检查对象身份是否相等。由于没有强制

  • 新的ES6箭头函数表示在某些情况下是隐式的: 表达式也是该函数的隐式返回值。 在什么情况下,我需要将与ES6箭头函数一起使用?

  • 问题内容: 我有一个将客户发送到另一个站点来处理付款的应用程序。客户之外的另一个站点在我们的服务器上调用一个页面,让我们知道付款的状态。被调用页面会检查付款应用程序提供的参数,并检查我们是否知道该交易。然后,它更新数据库以反映状态。这一切都无需与客户进行任何互动即可完成。 我个人选择将此功能实现为JSP,因为将文件拖放到文件系统中比编译和打包文件然后将条目添加到配置文件中要容易得多。 考虑到页面的

  • 我过去常常将用于后台任务,但后来发现建议使用,因为它们的寿命更长。 现在,在阅读时,我发现您可以在没有ui的情况下使用fragments来运行后台任务。

  • 问题内容: 我真的想不出python为什么需要关键字的任何原因(大多数语言似乎没有类似的关键字)。例如,可以删除变量而不是删除变量。从字典中删除时,可以添加一个方法。 是否有任何理由保留在python中,或者它是Python的垃圾收集日的痕迹? 问题答案: 首先,除了局部变量,你还可以进行其他操作 两者都应该明显有用。其次,del对局部变量使用可使意图更清晰。比较: 至 我知道在这种情况下,目的是