in 参数修饰符(C# 参考)in parameter modifier (C# Reference)

in 关键字通过引用传递参数。The in keyword causes arguments to be passed by reference. 它让形参成为实参的别名,这必须是变量。It makes the formal parameter an alias for the argument, which must be a variable. 换而言之,对形参执行的任何操作都是对实参执行的。In other words, any operation on the parameter is made on the argument. 它类似于 refout 关键字,不同之处在于 in 参数无法通过调用的方法进行修改。It is like the ref or out keywords, except that in arguments cannot be modified by the called method. out 参数必须由调用的方法进行修改,这些修改在调用上下文中是可观察的,而 ref 参数是可以修改的。Whereas ref arguments may be modified, out arguments must be modified by the called method, and those modifications are observable in the calling context.

int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument);     // value is still 44

void InArgExample(in int number)
{
    // Uncomment the following line to see error CS8331
    //number = 19;
}

前面的示例说明调用站点处通常不需要 in 修饰符。The preceding example demonstrates that the in modifier is usually unnecessary at the call site. 仅在方法声明中需要它。It is only required in the method declaration.

备注

in 关键字还作为 foreach 语句的一部分,或作为 LINQ 查询中 join 子句的一部分,与泛型类型参数一起使用来指定该类型参数为逆变。The in keyword can also be used with a generic type parameter to specify that the type parameter is contravariant, as part of a foreach statement, or as part of a join clause in a LINQ query. 有关在这些上下文使用 in 关键字的详细信息,请参阅 in,其中提供了所有这些用法的链接。For more information on the use of the in keyword in these contexts, see in, which provides links to all those uses.

作为 in 参数传递的变量在方法调用中传递之前必须进行初始化。Variables passed as in arguments must be initialized before being passed in a method call. 但是,所调用的方法可能不会分配值或修改参数。However, the called method may not assign a value or modify the argument.

in 参数修饰符可在 C# 7.2 及更高版本中使用。The in parameter modifier is available in C# 7.2 and later. 以前的版本生成编译器错误 CS8107(“‘readonly 引用’功能在 C# 7.0 中不可用。Previous versions generate compiler error CS8107 ("Feature 'readonly references' is not available in C# 7.0. 请使用语言版本 7.2 或更高版本。”)若要配置编译器语言版本,请参阅选择 C# 语言版本Please use language version 7.2 or greater.") To configure the compiler language version, see Select the C# language version.

inrefout 关键字不被视为用于重载决议的方法签名的一部分。The in, ref, and out keywords are not considered part of the method signature for the purpose of overload resolution. 因此,如果唯一的不同是一个方法采用 refin 参数,而另一个方法采用 out 参数,则无法重载这两个方法。Therefore, methods cannot be overloaded if the only difference is that one method takes a ref or in argument and the other takes an out argument. 例如,以下代码将不会编译:The following code, for example, will not compile:

class CS0663_Example
{
    // Compiler error CS0663: "Cannot define overloaded 
    // methods that differ only on in, ref and out".
    public void SampleMethod(in int i) { }
    public void SampleMethod(ref int i) { }
}

存在 in 时可以重载:Overloading based on the presence of in is allowed:

class InOverloads
{
    public void SampleMethod(in int i) { }
    public void SampleMethod(int i) { }
}

重载决策规则Overload resolution rules

通过理解使用 in 参数的动机,可以理解使用按值方法和使用 in 参数方法的重载决策规则。You can understand the overload resolution rules for methods with by value vs. in arguments by understanding the motivation for in arguments. 定义使用 in 参数的方法是一项潜在的性能优化。Defining methods using in parameters is a potential performance optimization. 某些 struct 类型参数可能很大,在紧凑的循环或关键代码路径中调用方法时,复制这些结构的成本就很高。Some struct type arguments may be large in size, and when methods are called in tight loops or critical code paths, the cost of copying those structures is critical. 方法声明 in 参数以指定参数可能按引用安全传递,因为所调用的方法不修改该参数的状态。Methods declare in parameters to specify that arguments may be passed by reference safely because the called method does not modify the state of that argument. 按引用传递这些参数可以避免(可能产生的)高昂的复制成本。Passing those arguments by reference avoids the (potentially) expensive copy.

为调用站点上的参数指定 in 通常为可选。Specifying in for arguments at the call site is typically optional. 按值传递参数和使用 in 修饰符按引用传递参数这两种方法并没有语义差异。There is no semantic difference between passing arguments by value and passing them by reference using the in modifier. 可以在调用站点选择 in 修饰符,因为你不需要指出参数值可能会改变。The in modifier at the call site is optional because you don't need to indicate that the argument's value might be changed. 在调用站点显式添加 in 修饰符以确保参数是按引用传递,而不是按值传递。You explicitly add the in modifier at the call site to ensure the argument is passed by reference, not by value. 显式使用 in 有以下两个效果:Explicitly using in has the following two effects:

首先,在调用站点指定 in 会强制编译器选择使用匹配的 in 参数定义的方法。First, specifying in at the call site forces the compiler to select a method defined with a matching in parameter. 否则,如果两种方法唯一的区别在于是否存在 in,则按值重载的匹配度会更高。Otherwise, when two methods differ only in the presence of in, the by value overload is a better match.

第二点,指定 in 会声明你想按引用传递参数。Second, specifying in declares your intent to pass an argument by reference. 结合 in 使用的参数必须代表一个可以直接引用的位置。The argument used with in must represent a location that can be directly referred to. outref 参数的相同常规规则适用:不得使用常量、普通属性或其他生成值的表达式。The same general rules for out and ref arguments apply: You cannot use constants, ordinary properties, or other expressions that produce values. 否则,在调用站点省略 in 就会通知编译器你将允许它创建临时变量,并按只读引用传递至方法。Otherwise, omitting in at the call site informs the compiler that you will allow it to create a temporary variable to pass by read-only reference to the method. 编译器创建临时变量以克服一些 in 参数的限制:The compiler creates a temporary variable to overcome several restrictions with in arguments:

  • 临时变量允许将编译时常数作为 in 参数。A temporary variable allows compile-time constants as in parameters.
  • 临时变量允许使用属性或 in 参数的其他表达式。A temporary variable allows properties, or other expressions for in parameters.
  • 存在从实参类型到形参类型的隐式转换时,临时变量允许使用实参。A temporary variable allows arguments where there is an implicit conversion from the argument type to the parameter type.

在前面的所有实例中,编译器创建了临时变量,用于存储常数、属性或其他表达式的值。In all the preceding instances, the compiler creates a temporary variable that stores the value of the constant, property, or other expression.

以下代码阐释了这些规则:The following code illustrates these rules:

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // OK, temporary variable created.
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // OK, temporary int created with the value 0
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // passed by readonly reference
Method(in i); // passed by readonly reference, explicitly using `in`

现在,假设可以使用另一种使用按值参数的方法。Now, suppose another method using by value arguments was available. 结果的变化如以下代码所示:The results change as shown in the following code:

static void Method(int argument)
{
    // implementation removed
}

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // Calls overload passed by value
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // Calls overload passed by value.
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // Calls overload passed by value
Method(in i); // passed by readonly reference, explicitly using `in`

最后一个是按引用传递参数的唯一方法调用。The only method call where the argument is passed by reference is the final one.

备注

为了简化操作,前面的代码将 int 用作参数类型。The preceding code uses int as the argument type for simplicity. 因为大多数新式计算机中的引用都比 int 大,所以将单个 int 作为只读引用传递没有任何好处。Because int is no larger than a reference in most modern machines, there is no benefit to passing a single int as a readonly reference.

in 参数的限制Limitations on in parameters

不能将 inrefout 关键字用于以下几种方法:You can't use the in, ref, and out keywords for the following kinds of methods:

  • 异步方法,通过使用 async 修饰符定义。Async methods, which you define by using the async modifier.
  • 迭代器方法,包括 yield returnyield break 语句。Iterator methods, which include a yield return or yield break statement.

C# 语言规范C# Language Specification

有关详细信息,请参阅 C# 语言规范For more information, see the C# Language Specification. 该语言规范是 C# 语法和用法的权威资料。The language specification is the definitive source for C# syntax and usage.

请参阅See also