C# 变量作用域

局部、块级、类级、静态,以及 `using` 声明的生命周期。

Beginner
Key Points变量在哪个 `{}` 里声明就活到那个 `{}` 结束;字段活到对象被回收。

三种作用域

C#
class Demo
{
    int field = 1;                  // 字段:整个对象生命周期
    static int shared = 2;          // 静态字段:整个进程

    void Run()
    {
        int local = 3;              // 局部变量:方法调用期间
        Console.WriteLine($"{field} {shared} {local}");

        for (int i = 0; i < 2; i++)
        {
            int inLoop = i * 10;    // 块级:只在 for 体内
            Console.WriteLine(inLoop);
        }
        // Console.WriteLine(inLoop);   // 编译错误
    }
}

new Demo().Run();
Output
1 2 3
0
10

嵌套与遮蔽

C#
int x = 1;
{
    int y = 2;
    Console.WriteLine($"{x} {y}");
}
// y 在这里不存在

// 同一作用域不能重复声明
// int x = 5;      // 编译错误

// 内层可以遮蔽外层(有警告 CS0136 在部分场景)
void Test()
{
    int n = 1;
    if (true)
    {
        int m = n + 1;      // 用外层
        Console.WriteLine(m);
    }
}
Test();
Output
1 2
2

C# forbids declaring a local with the same name as an enclosing local (to avoid ambiguity) — stricter than JavaScript. When a field and a local share a name the local wins; use this.x for the field.

using 声明

C#
// 传统写法:块级,出块就释放
using (var reader = File.OpenText("a.txt"))
{
    Console.WriteLine(reader.ReadLine());
}

// C# 8 起:using 声明,活到所在作用域结束
using var writer = File.CreateText("b.txt");
writer.WriteLine("内容");
// 方法结束时自动 Dispose

using var saves a level of indentation but lives to the end of the enclosing block. In a long method where you want an early release, use the block form.

静态字段的坑

C#
class Counter
{
    public static int Count;        // 所有实例共享
    public int InstanceCount;

    public Counter()
    {
        Count++;
        InstanceCount++;
    }
}

new Counter();
new Counter();
var c = new Counter();

Console.WriteLine(Counter.Count);
Console.WriteLine(c.InstanceCount);
Output
3
1
Static fields are not thread-safe. Count++ is not atomic, so two threads can lose a count. Guard shared state with a lock or use Interlocked.Increment.

值类型与作用域的交互

C#
void Modify(int x)
{
    x = 999;                // 改的是副本
}

int n = 1;
Modify(n);
Console.WriteLine(n);       // 还是 1

void ModifyRef(ref int x)
{
    x = 999;
}
ModifyRef(ref n);
Console.WriteLine(n);       // 变了
Output
1
999

ref lets a method operate on the caller's variable itself. out is similar but requires assignment inside. Both are C#'s explicit pass-by-reference.

Friends & Partners