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