C# 7.2 introduced the in
modifier for passing arguments by reference with the guarantee that the recipient will not modify the parameter.
This article says:
You should never use a non-readonly struct as the in parameters because it may negatively affect performance and could lead to an obscure behavior if the struct is mutable
What does this mean for built-in primitives such as int
, double
?
I would like to use in
to express intent in code, but not at the cost of performance losses to defensive copies.
Questions
in
arguments and not have defensive copies made?DateTime
, TimeSpan
, Guid
, ... considered readonly
by the JIT?
A quick test shows that, currently, yes, a defensive copy is created for built-in primitive types and structs.
Compiling the following code with VS 2017 (.NET 4.5.2, C# 7.2, release build):
using System;
class MyClass
{
public readonly struct Immutable { public readonly int I; public void SomeMethod() { } }
public struct Mutable { public int I; public void SomeMethod() { } }
public void Test(Immutable immutable, Mutable mutable, int i, DateTime dateTime)
{
InImmutable(immutable);
InMutable(mutable);
InInt32(i);
InDateTime(dateTime);
}
void InImmutable(in Immutable x) { x.SomeMethod(); }
void InMutable(in Mutable x) { x.SomeMethod(); }
void InInt32(in int x) { x.ToString(); }
void InDateTime(in DateTime x) { x.ToString(); }
public static void Main(string[] args) { }
}
yields the following result when decompiled with ILSpy:
...
private void InImmutable([System.Runtime.CompilerServices.IsReadOnly] [In] ref MyClass.Immutable x)
{
x.SomeMethod();
}
private void InMutable([System.Runtime.CompilerServices.IsReadOnly] [In] ref MyClass.Mutable x)
{
MyClass.Mutable mutable = x;
mutable.SomeMethod();
}
private void InInt32([System.Runtime.CompilerServices.IsReadOnly] [In] ref int x)
{
int num = x;
num.ToString();
}
private void InDateTime([System.Runtime.CompilerServices.IsReadOnly] [In] ref DateTime x)
{
DateTime dateTime = x;
dateTime.ToString();
}
...
(or, if you prefer IL:)
IL_0000: ldarg.1
IL_0001: ldobj [mscorlib]System.DateTime
IL_0006: stloc.0
IL_0007: ldloca.s 0
IL_0009: call instance string [mscorlib]System.DateTime::ToString()
IL_000e: pop
IL_000f: ret