C#7.2为参数引入了in
修饰符,这对于结构,特别是对于只读结构非常有意义。
也可以将它用作参考类型
void Method(in StringBuilder value) { }
默认情况下,作为引用类型通过引用传递,上面示例中的in
只是一个冗余修饰符吗?
value = null
时禁止使用 in
,这是否意味着只通过将原始引用传递给堆位置并阻止更改来保留引用地址的副本?
答案 0 :(得分:9)
in
以与ref
完全相同的方式编译为IL,除了in
参数标有IsReadOnly
属性。
这意味着in
的行为与ref
完全相同,但编译器(不是运行时)强制您不将值赋给in
参数。
因此,正如您正确指出的那样 - in
referenece-type参数通过引用传递(这意味着引用不会被复制并指向原始位置),但编译器会阻止您更改它。对于引用类型,我并没有真正看到它有多大用处,但是至少为了保持一致性并没有什么用处。
答案 1 :(得分:2)
根据我对official documentation的理解,这意味着传递给方法的参数不会在方法本身内改变:
in
关键字指定您通过引用传递参数 ,并且被调用方法不会修改传递给它的值。
将in
关键字与值类型一起使用时,意味着不是通过值传递参数(意味着创建值的新副本),而是通过引用传递 - 因此它避免了不必要的复制。
答案 2 :(得分:2)
虽然其他两个答案是正确的,in
参数在结果IL中最终为ref
参数,但应注意这可以防止复制值。这仅适用于只读结构。
为了证明这一点,请考虑以下代码:
using System;
public struct S1
{
public int A;
public void ChangeA(int a) => A = a;
}
public static class Program
{
static void Main()
{
var s1 = new S1 { A = 1 };
S1Foo(in s1);
Console.WriteLine(s1.A);
}
private static void S1Foo(in S1 s) => s.ChangeA(2);
}
由于我们通过引用传递s1
,因此可以合理地假设S1Foo
在调用ChangeA
时会更改s1
的内容。 This doesn't happen though。原因是复制s1
值并通过引用传递副本,以防止通过in
参数对结构进行此类修改。
If we decompile the resultant IL,您会看到代码最终为:
public static class Program
{
private static void Main()
{
S1 s = default(S1);
s.A = 1;
S1 s2 = s;
Program.S1Foo(ref s2);
Console.WriteLine(s2.A);
}
private static void S1Foo([IsReadOnly] [In] ref S1 s)
{
S1 s2 = s;
s2.ChangeA(2);
}
}
但是,如果我们使用readonly struct
编写类似的代码,则不会发生复制。我说类似,因为不可能编写相同的代码作为字段和属性必须只读在readonly结构(线索在名称中):
using System;
public readonly struct S2
{
private readonly int _a;
public int A => _a;
public S2(int a) => _a = a;
public void ChangeA(int a) { }
}
public static class Program
{
static void Main()
{
var s2 = new S2(1);
S2Foo(in s2);
Console.WriteLine(s2.A);
}
private static void S2Foo(in S2 s) => s.ChangeA(2);
}
然后no copy occurs in the resultant IL。
总结如下:
in
实际上是readonly ref
,答案 3 :(得分:0)
在引用类型中,我能想到的唯一有用的东西就是带有约束的泛型函数。
public interface IIntContainer
{
int Value { get; }
}
public readonly struct LargeStruct : IIntContainer
{
public readonly int val0;
public readonly int val1;
// ... lots of other fields
public readonly int val20;
public int Value => val0;
}
public class SmallClass : IIntContainer
{
public int val0;
public int Value => val0;
}
public static class Program
{
static void Main()
{
DoSomethingWithValue(new LargeStruct());
DoSomethingWithValue(new SmallClass());
}
public static void DoSomethingWithValue<T>(in T container) where T : IIntContainer
{
int value = container.Value;
// Do something with value...
}
}