"/>

C#中的匿名未附加块的值是什么?

C#中的匿名未附加块的值是什么?

What is the value of an anonymous unattached block in C#?

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

1
2
3
4
5
6
7
8
9
10
11
12
    public void TestMethod()
    {
        {
            string x ="test";
            string y = x;

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

该代码将编译并运行,就像main方法中的括号不存在一样。 还要注意块内的块。

是否存在一种有价值的方案? 我还没有找到任何东西,但很好奇听到别人的发现。


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

雷·海斯(Ray Hayes)指出,.NET垃圾收集器不会立即收集范围外的对象,因此范围界定是主要的好处。


例如,如果您想重用变量名,通常就不能重用变量名。
这是无效的

1
2
3
4
5
        int a = 10;
        Console.WriteLine(a);

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

但这是:

1
2
3
4
5
6
7
8
    {
        int a = 10;
        Console.WriteLine(a);
    }
    {
        int a = 20;
        Console.WriteLine(a);
    }

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

1
2
3
4
5
6
7
8
    {
        //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


语句是简单语句或块是解析器规则的副产品。也就是说,只要有一条语句,就可以使用块。

例如

1
2
3
4
5
6
7
if (someCondition)
 SimpleStatement();

if (SomeCondition)
{
   BlockOfStatements();
}

其他人指出,变量声明一直在范围内,直到包含块的末尾。临时var的作用域较短是很好的选择,但是我从来不需要自己使用块来限制变量的作用域。有时,您可以在"使用"语句下面使用一个块。

因此,通常它没有价值。


使其存在的一个实际原因是,当您没有必要为该块引入任何其他原因时,是否要限制某个变量的范围。实际上,这实际上是没有用的。

就我个人而言,我的猜测是,从语言/编译器的角度来看,可以说可以在期望语句的任何位置放置一个块,这很容易,而且他们只是竭尽全力阻止您在没有if的情况下使用它/ for /方法声明/等

考虑一下Eric Lippert最近的博客文章的开头。 if语句后没有单个语句或大括号中包含的多个语句,仅后面有一个语句。每当将0到N的语句括在花括号中时,都会使该部分代码等效(从语言解析器的角度来看)一个语句。同样的做法也适用于所有循环结构,尽管正如博客文章的要点解释的那样,它不适用于try / catch / finally块。

从这种观点出发解决块时,问题就变成了:"是否有充分的理由阻止在任何可以使用单个语句的地方使用块?"答案是"不"。


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

1
2
3
4
5
6
7
8
9
10
11
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#中,我们可以使用作用域块,以便在每种情况下声明的项目仅在这种情况下在作用域中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
switch( value )
{
    case const1:
    {
        int i = GetValueSomeHow();
        //do something
        return i.ToString();
    }

    case const2:
    {
        int i = GetADifferentValue();
        //no exception now
        return SomeFunctionOfInt( i );
    }
 ...

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


据我所知,仅从组织的角度来看这才有用。我这样做真的没有任何逻辑上的价值。也许有人会举一个恰当的例子。


除了语义之外,对于范围和垃圾回收来说,这没有其他价值,在此有限的示例中,它们都不重要。如果您认为它可以为您自己和/或其他人使代码更清晰,那么您肯定可以使用它。但是,在代码中用于语义澄清的更普遍接受的约定通常只在选项内嵌注释中使用换行符:

1
2
3
4
5
6
7
8
9
10
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;
}

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


在C#中(如c / c ++ / java),花括号表示作用域。这决定了变量的寿命。当到达右括号时,该变量立即可用于垃圾回收。在c ++中,如果var表示实例,则将导致调用类的析构函数。

至于用法,唯一可能的用途是释放一个大对象,但是将tbh设置为null会产生相同的效果。我怀疑以前的用法可能只是为了使C ++程序员在熟悉而舒适的领域中迁移到托管代码。如果确实要在c#中调用"析构函数",则通常要实现IDisposable接口并使用" using(var){...}"模式。

爱信


这样做的原因之一是变量" z"和" zz"在该内部块的末尾将无法进行编码。当您使用Java进行此操作时,JVM会为内部代码推送堆栈框架,并且这些值可以存在于堆栈中。当代码退出该块时,将弹出堆栈框架,而这些值将消失。根据所涉及的类型,这可以使您不必使用堆和/或垃圾收集。


推荐阅读