C# 类的in修饰符有什么意义

C# 类的in修饰符有什么意义,c#,c#-7.2,C#,C# 7.2,C#7.2在中为参数引入了修饰符,这对结构特别是只读结构非常有意义 还允许将其用于引用类型 void Method(in StringBuilder value) { } 由于默认情况下引用类型是通过引用传递的,因此上例中的中的是否只是一个冗余修饰符 当您在中使用时,value=null是禁止的,这是否意味着它仅通过将原始引用传递到堆位置并阻止更改,就可以节省引用地址的副本?中的以与ref完全相同的方式编译为IL,除了中的,参数用IsReadOnly属性标记 这意味着中的的行为与ref完全相同

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

还允许将其用于引用类型

void Method(in StringBuilder value) { }
由于默认情况下引用类型是通过引用传递的,因此上例中的
中的
是否只是一个冗余修饰符


当您在
中使用
时,
value=null
是禁止的,这是否意味着它仅通过将原始引用传递到堆位置并阻止更改,就可以节省引用地址的副本?

中的
以与
ref
完全相同的方式编译为IL,除了
中的
,参数用
IsReadOnly
属性标记

这意味着
中的
的行为与
ref
完全相同,但编译器(不是运行时)强制您不要在
参数中为
赋值


所以,正如您正确指出的-
referencetype参数中的
是通过引用传递的(这意味着引用不会被复制并指向原始位置),但编译器阻止您更改它。我真的不认为它对引用类型有多大用处,但至少在一致性方面这样做不会有什么坏处。

从我的理解来看,这意味着传递给方法的参数在方法本身内部不会更改:

关键字中的
指定通过引用传递参数,调用的方法不会修改传递给它的值


在将
中的
关键字与值类型一起使用时,这意味着不按值传递参数(意味着创建值的新副本),它是通过引用传递的-因此它避免了不必要的复制。

虽然其他两个答案是正确的,即
中的
参数最终在结果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
的内容。原因是复制了
s1
值并通过引用传递副本,以防止通过
中的
参数对结构进行此类修改

,您会看到代码的结尾是:

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);
    }
}
但是,如果我们使用
只读结构编写类似的代码,则不会发生复制。我说的是类似的,因为在只读结构中,字段和属性必须是只读的,所以不可能编写相同的代码(线索在名称中):

那么

总而言之:

  • 中的
    实际上是一个
    只读参考
  • 通过引用传递值(或引用)
  • 编译器防止修改该引用的字段和属性以帮助强制其可读性
  • 为了进一步强制参数的只读性质,在将对副本的引用传递给方法之前,将复制非只读结构。只读结构不会出现这种情况

  • 对于引用类型,我能想到的唯一有用的东西是带有约束的泛型函数

    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...
        }
    }
    
    公共接口IIntContainer
    {
    int值{get;}
    }
    公共只读结构大结构:IIntContainer
    {
    公共只读int-val0;
    公共只读int-val1;
    //…还有很多其他领域
    公共只读int-val20;
    公共int值=>val0;
    }
    公共类SmallClass:IIntContainer
    {
    公共int val0;
    公共int值=>val0;
    }
    公共静态类程序
    {
    静态void Main()
    {
    DoSomethingWithValue(新的最大结构());
    DoSomethingWithValue(新的SmallClass());
    }
    公共静态void DoSomethingWithValue(在T容器中),其中T:IIntContainer
    {
    int value=container.value;
    //做一些有价值的事情。。。
    }
    }
    
    呼叫@EricLippert…有帮助吗<代码>内参数名称也可用于参考类型或内置数值。但是,这两种情况下的好处都很小(如果有的话)。
    我怀疑主要的好处是允许您编写同时使用
    泛型和
    中的
    的代码(因此允许
    中的
    使用引用类型意味着您不需要将泛型代码约束到结构)。@mjwills这是一个很好的猜测,我还想知道它是否真的避免了复制指针(不是为了提高性能,只是出于好奇),它只是对值类型参数的优化。它承诺C#编译器不必生成额外的代码来确保值被复制。大型结构可以生成更快的代码,但复制会带来不利影响。它通常不适用于引用类型。禁止它也没有用,当程序员决定更改类型声明时会让他们头疼。谢谢,我知道它只是一个带有属性的
    ref
    ,但我在写问题时没有考虑它。它具有相同的行为是有意义的。在
    中使用
    传递引用类型参数将其作为引用传递给引用,就像通过
    ref
    传递它一样。它改变了检索值的方式,在某些情况下可能会有不同的行为。在第一个示例中,我理解创建结构副本以保护结构免受任何更改,但在
    S1Foo
    方法中,它为什么要创建第二个副本并在此副本上调用
    ChangeA
    方法,
    s
    已经是从
    Main
    方法发送的副本的引用。如果调用传入引用
    s
    上的
    Change
    方法,会发生什么情况?
    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...
        }
    }