C# 7.2 introduces the in
modifier for parameters which makes perfect sense for structs and in particular for readonly structs.
It is also allowed to use it for a reference type
void Method(in StringBuilder value) { }
As reference types are passed by reference by default, is the in
in the example above just a redundant modifier?
value = null
is forbidden when you use in
, does it mean that it spares also the copy of the reference address by just passing the original reference to the heap location and blocking changes?
in
is compiled to IL in exactly the same way as ref
, except in
argument is marked with IsReadOnly
attribute.
That means in
behaves exactly as ref
, but compiler (not runtime) enforces that you don't assign value to in
argument.
So, as you correctly pointed out - in
referenece-type argument is passed by reference (which means reference is not copied and points to original location), but compiler prevents you from changing it. I don't really see much use for it for reference types, but it won't hurt to have that, at least for consistency.
Whilst the other two answers are correct that in
parameters end up as ref
parameters in the resultant IL, care should be taken with the claim that this prevents the value being copied. This only holds true for readonly structs.
To demonstrate this, consider the following piece of code:
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);
}
Since we are passing s1
by reference, one might reasonably assume that S1Foo
, in calling ChangeA
would then change the contents of s1
. This doesn't happen though. The reason being that the s1
value is copied and a copy is passed by reference, to prevent such modifications of structs via in
parameters.
If we decompile the resultant IL, you see that the code ends up as:
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);
}
}
However, if we write similar code using a readonly struct
, then no copying occurs. I say similar as it isn't possible to write the same code as fields and property have to be readonly in a readonly struct (the clue is in the name):
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);
}
Then no copy occurs in the resultant IL.
So in summary:
in
is effectively a readonly ref
,From what I understand from official documentation, it means that arguments passed to the method will not be changed inside the method itself:
The
in
keyword specifies that you are passing the parameter by reference and the called method does not modify the value passed to it.
when using the in
keyword with value types, it means that instead of passing the argument by value (meaning creating a new copy of the value), it is passed by reference - so it avoids the unnecessary copying.
The only useful thing I can think of for in with reference types would be generic functions with constraints.
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...
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With