类的in修饰符有什么意义

时间:2018-03-07 10:25:40

标签: c# c#-7.2

C#7.2为参数引入了in修饰符,这对于结构,特别是对于只读结构非常有意义。

也可以将它用作参考类型

void Method(in StringBuilder value) { }

默认情况下,作为引用类型通过引用传递,上面示例中的in只是一个冗余修饰符吗?

使用value = null时禁止使用

in,这是否意味着只通过将原始引用传递给堆位置并阻止更改来保留引用地址的副本?

4 个答案:

答案 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

总结如下:

  1. in实际上是readonly ref
  2. 值(或引用)通过引用传递,
  3. 编译器会阻止修改该引用的字段和属性,以帮助强制执行其readonly-ness,
  4. 要进一步强制执行参数的只读性质,则在将对副本的引用传递给方法之前复制非只读结构。对于只读结构,不会发生这种情况。

答案 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...
    }
}