声明语句

声明语句声明新的局部变量、局部常量或 reference 局部变量

C# 语言参考记录了最近发布的 C# 语言版本。 它还包含即将发布的语言版本公共预览版中功能的初始文档。

本文档标识了在语言的最后三个版本或当前公共预览版中首次引入的任何功能。

小窍门

若要查找 C# 中首次引入功能时,请参阅 有关 C# 语言版本历史记录的文章。

若要声明局部变量,请指定其类型并提供其名称。 可以在一个语句中声明多个相同类型的变量,如以下示例所示:

string greeting;
int a, b, c;
List<double> xs;

在声明语句中,还可以使用变量的初始值对该变量进行初始化:

string greeting = "Hello";
int a = 3, b = 2, c = a + b;
List<double> xs = new();

前面的示例显式指定了变量的类型。 还可以让编译器从其初始化表达式推断出变量的类型。 为此,请使用 var 关键字而不是类型的名称。 有关详细信息,请参阅隐式类型的局部变量部分。

若要声明局部常量,请使用 const 关键字,如以下示例所示:

const string Greeting = "Hello";
const double MinLimit = -10.0, MaxLimit = -MinLimit;

声明局部常量时,还必须对其进行初始化。

有关 reference 局部变量的信息,请参阅 Reference 变量部分。

隐式类型本地变量

声明局部变量时,可以让编译器从初始化表达式推断出变量的类型。 为此,请使用 var 关键字而不是类型的名称:

var greeting = "Hello";
Console.WriteLine(greeting.GetType());  // output: System.String

var a = 32;
Console.WriteLine(a.GetType());  // output: System.Int32

var xs = new List<double>();
Console.WriteLine(xs.GetType());  // output: System.Collections.Generic.List`1[System.Double]

如前面的示例所示,隐式类型局部变量是强类型化的。

注意

在已启用的var中使用 ,且初始化表达式的类型为引用类型时,即使初始化表达式的类型不可为 null,编译器也始终推断出可为 null 的引用类型。

var 的常见用途是用于构造函数调用表达式。 使用 var 此方法可以避免在变量声明和对象实例化中重复类型名称,如以下示例所示:

var xs = new List<int>();

可以使用由目标确定类型的 new 表达式作为替代方法:

List<int> xs = new();
List<int>? ys = new();

使用 匿名类型时,必须使用隐式类型局部变量。 以下示例显示了一个查询表达式,该表达式使用匿名类型保存客户的姓名和电话号码:

var fromPhoenix = from cust in customers
                  where cust.City == "Phoenix"
                  select new { cust.Name, cust.Phone };

foreach (var customer in fromPhoenix)
{
    Console.WriteLine($"Name={customer.Name}, Phone={customer.Phone}");
}

在前面的示例中,无法显式指定 fromPhoenix 变量的类型。 类型为 IEnumerable<T>,但在本例中 T 为匿名类型,无法提供其名称。 这就是需要使用 var 的原因。 出于同一原因,在 var 语句中声明 customer 迭代变量时必须使用 foreach

有关隐式类型局部变量的详细信息,请参阅 隐式类型局部变量

在模式匹配中,在 var中使用 var 关键字。

Reference 变量

声明局部变量并在变量类型之前添加 ref 关键字时,声明 reference 变量,或 局部变量:

ref int aliasOfvariable = ref variable;

reference 变量是引用另一个变量(称为引用)的变量。 也就是说,reference 变量是其引用的别名。 将值分配给引用变量时,请将该值分配给引用。 读取引用变量的值时,将返回引用的值。 以下示例演示了该行为:

int a = 1;
ref int aliasOfa = ref a;
Console.WriteLine($"(a, aliasOfa) is ({a}, {aliasOfa})");  // output: (a, aliasOfa) is (1, 1)

a = 2;
Console.WriteLine($"(a, aliasOfa) is ({a}, {aliasOfa})");  // output: (a, aliasOfa) is (2, 2)

aliasOfa = 3;
Console.WriteLine($"(a, aliasOfa) is ({a}, {aliasOfa})");  // output: (a, aliasOfa) is (3, 3)

使用 ref 赋值运算符= ref 更改 reference 变量的引用,如以下示例所示:

void Display(int[] s) => Console.WriteLine(string.Join(" ", s));

int[] xs = [0, 0, 0];
Display(xs);

ref int element = ref xs[0];
element = 1;
Display(xs);

element = ref xs[^1];
element = 3;
Display(xs);
// Output:
// 0 0 0
// 1 0 0
// 1 0 3

在前面的示例中,element reference 变量初始化为第一个数组元素的别名。 然后,ref 将被重新分配,以引用最后一个数组元素。

可以定义 ref readonly 局部变量。 不能为 ref readonly 变量赋值。 但是,可以 ref 重新分配此类引用变量,如以下示例所示:

int[] xs = [1, 2, 3];

ref readonly int element = ref xs[0];
// element = 100;  error CS0131: The left-hand side of an assignment must be a variable, property or indexer
Console.WriteLine(element);  // output: 1

element = ref xs[^1];
Console.WriteLine(element);  // output: 3

可以将引用返回分配给 reference 变量,如以下示例所示:

using System;

public class NumberStore
{
    private readonly int[] numbers = [1, 30, 7, 1557, 381, 63, 1027, 2550, 511, 1023];

    public ref int GetReferenceToMax()
    {
        ref int max = ref numbers[0];
        for (int i = 1; i < numbers.Length; i++)
        {
            if (numbers[i] > max)
            {
                max = ref numbers[i];
            }
        }
        return ref max;
    }

    public override string ToString() => string.Join(" ", numbers);
}

public static class ReferenceReturnExample
{
    public static void Run()
    {
        var store = new NumberStore();
        Console.WriteLine($"Original sequence: {store.ToString()}");
        
        ref int max = ref store.GetReferenceToMax();
        max = 0;
        Console.WriteLine($"Updated sequence:  {store.ToString()}");
        // Output:
        // Original sequence: 1 30 7 1557 381 63 1027 2550 511 1023
        // Updated sequence:  1 30 7 1557 381 63 1027 0 511 1023
    }
}

在前面的示例中,GetReferenceToMax 方法指的是 returns-by-ref 方法。 它不返回最大值本身,而是引用返回,该引用返回是包含最大值的数组元素的别名。 Run 方法将引用返回分配给 max reference 变量。 然后,通过分配给 max,它会更新 store 实例的内部存储。 还可以定义 ref readonly 方法。 ref readonly 方法的调用方无法为其引用返回赋值。

foreach 语句的迭代变量可以是引用变量。 有关详细信息,请参阅foreach一文的 语句部分。

在性能关键型方案中,使用 reference 变量和返回可能会避免成本高昂的复制操作,从而提高性能。

编译器确保 reference 变量在时间上不超过其引用,并在其整个生存期内保持有效。 有关详细信息,请参阅 C# 语言规范中的 Ref safe 上下文部分。

有关 ref 字段的信息,请参阅 ref 结构类型一文的 ref 字段部分。

scoped ref

上下文关键字 scoped 限制值的生存期。 scoped 修饰符将 ref-safe-to-escape 或 safe-to-escape 生存期分别限制为当前方法。 通过添加 scoped 修饰符,可以断言代码不会延长变量的生存期。

应用于 scoped 参数或局部变量。 当类型为 ref struct 时,scoped可以将修饰符应用于参数和局部变量。 否则,请仅将 scoped 修饰符应用于本地 引用变量。 该规则包括用修饰符声明ref的局部变量,以及用inrefout修饰符或修饰符声明的参数。

当类型为 scoped 时,使用 thisstruct 参数和 out 参数声明的方法将 ref 修饰符隐式添加到 ref struct

C# 语言规范

有关更多信息,请参阅 C# 语言规范的以下部分:

有关 scoped 修饰符的详细信息,请参阅低级别 struct 改进建议说明。

另请参阅