Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between in and ref readonly parameters

Tags:

c#

ref

c#-12.0

What is the difference between in and ref readonly parameters?

I found both in and ref readonly parameters make the parameter readonly and it cannot be modified in the called method. Do they have a similar function?

like image 844
Simant Avatar asked Aug 31 '25 18:08

Simant


2 Answers

The two existing answers are misleading about the behavior of in. They suggest that specifying in at the call site of a method with an in parameter results in different behavior than not specifying in at the call site. However, given this API:

public unsafe struct LargeStruct
{
    public fixed int Bar[32];
}

public void M(in LargeStruct largeStruct)
{
}

The following two call sites will produce identical code:

LargeStruct largeStruct = default;
M(largeStruct);
LargeStruct largeStruct = default;
M(in largeStruct);

Compare the compiled code on SharpLab.

Though, there is some sense in which specifying in at the call site can make a difference, which is that specifying in prevents passing rvalues as arguments.

An rvalue is basically anything that can go on the right-hand side of an assignment but not the left-hand side, such as a constant or return value of a method.

For example, this will compile:

M(default);

but this will not:

M(in default);

The actual difference between in and ref readonly is only about the compile-time warnings that will be produced at call sites. There is no difference in runtime behavior or even in compile-time errors, just compile-time warnings.

Others have linked to the table in Microsoft documentation that precisely details all the differences in compile-time warnings, but the main point is that ref readonly will cause compile-time warnings for rvalue arguments, whereas in will allow rvalue arguments without warnings.

To see why this makes ref readonly useful, take a look at one example of it in a .NET runtime API:

Unsafe.IsNullRef<T>(ref readonly T source)

According to the docs, this API "determines if a given managed pointer to a value of type T is a null reference." Note that the API does NOT check whether the argument value is null, just whether the argument ref is null. A sensible use of the API might look like:

public void DoUnsafeStuff(ref string[] stringArray)
{
    if (Unsafe.IsNullRef(ref stringArray))
    {
        throw new ArgumentNullException(nameof(stringArray));
    }

    ...
}

Now, consider if Microsoft had defined the parameter of Unsafe.IsNullRef() as in instead of ref readonly. That would have allowed this to compile without warning:

public void DoUnsafeStuff(ref string[] stringArray)
{
    if (Unsafe.IsNullRef(stringArray.FirstOrDefault()))
    {
        throw new ArgumentNullException(nameof(stringArray));
    }

    ...
}

That code doesn't make conceptual sense, though, because there is no programmer-visible ref location associated with that stringArray.FirstOrDefault() return value, so the result will always be false. If someone wrote that code, they have clearly made a mistake, possibly incorrectly believing that Unsafe.IsNullRef() will check whether the value passed to it is null, as opposed to the ref passed to it.

So, ref readonly is beneficial because it enables triggering a compile-time warning in a scenario like this where the developer has clearly made a mistake.

like image 166
mrok Avatar answered Sep 02 '25 07:09

mrok


Imagine you require a reference to a value instead of a value as an argument to a method, but the struct you want to pass is a readonly struct. You have three options as of right now to pass by reference:

  1. The ref parameter: This is not an option for you, as you are passing a readonly struct which cannot be modified, but because you could technically modify a ref parameter, the compiler must generate a copy of the readonly struct to guarantee, that the readonly struct is not modified.

  2. The in parameter: This one is inapplicable in your case, because while it avoids copying the entire struct, and prohibits modificiation, there is no guarantee, that your parameter will actually be passed by reference - you can omit the in keyword when passing the parameter, to pass it by value. This means, that if you need to have a reference for an unsafe method for example, this would not work. The reason for the in keyword, is not for a parameter to be passed by readonly reference, but to avoid allocation. This often means passing a parameter by reference, but is not required.

  3. The readonly ref parameter solves the above posed issue: It is guaranteed by a compiler warning, that the value must be passed by reference and there will not be a protection copy, because you cannot modify the parameter.

To conclude: Only the readonly ref parameter will work, if you need to have a reference to a readonly struct. The in parameter does not guarantee that the parameter is passed by reference, and the ref parameter allows for modification, what means that the compiler has to copy the object to ensure, that it is not modified.


Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!