Why use parameter modifiers?

Asked

Viewed 85 times

4

While writing functions and subroutines, we use parameters to interact with the function algorithm. These parameters can have several purposes, such as passing a value, a reference, or leaving a value or reference.

At least in C# we have the in, the ref and the out. The first, causes only the value to be passed to the argument, the second, causes the value and the reference to be passed, and changes its value and reference, while the third, causes nothing to enter, only the reference (with value) comes out.

But none is mandatory, all are optional.

Why and when to use them?

Imagine the simplest scenario for this: a procedure that sums two integers. We can do with return, reference or assignment. Which to use?

// situação 1 - retorno na função sem modificadores
public static int Soma(int a, int b) {
    return a + b;
} // uso: z = Soma(x, y);

// situação 2 - retorno na função com modificadores de entrada
public static int Soma(in int a, in int b) {
    return a + b;
} // uso: z = Soma(x, y);

// situação 3 - sem retorno, com modificador de entrada e saída
public static void Soma(in int a, in int b, out int c) {
    c = a + b;
} // uso: Soma(x, y, out var z);

// situação 4 - sem retorno, com modificador de entrada e referência
public static void Soma(in int a, in int b, ref int c) {
    c = a + b;
} // uso: int z = 0; Soma(x, y, ref z);
```

Outro exemplo, é o retorno de mais de uma variável por uma rotina:

```C#
// usando tuplas
public static (float val, float abs, float resto) Dividir (float a, float b) {
    (float val, float abs, float resto) saida;
    saida.val = a / b;
    saida.abs = Math.Abs(a / b);
    saida.resto = a % b;
    return saida;
}

// usando modificadores de parâmetros
public static void Dividir(in float a, in float b, out float val, out float abs, out float resto) {
    val = a / b;
    abs = Math.Abs(a / b);
    resto = a % b;
}

As we can see above, there are several ways to do the same thing, but I can’t understand which and when to use it correctly.

Finally,

  • When to use in, out and ref?
  • Why should we use them?
  • Which fits best in each situation as described above?

1 answer

3


Their functioning has already been described in What are the out and ref parameters (the example of ref is artificial and not very good, is the same of the question here) and What good is this 'in' in C#? and even a little in What are pointers?, so I won’t go into too much detail.

The first, causes only the value to be passed to the argument

This is incorrect, the in only promises that it will receive an object by reference and that it will not modify it.

The examples with in should not use any of this, there are no copy gains because the pointer size is even larger than the whole dice. If you don’t have a large object don’t use the in.

The third example is no longer necessary as demonstrated by the question itself and in fact neither tuple should be used (the example is very bad, until the use of tuple is wrong there).

The last example is a classic case of method that does too many things and should be unlocked in several.

The use of the three always occurs with objects that we don’t want to copy directly for some reason.

The main reason is that this object is too big to be copied (in general more than 16 bytes) what would cost expensive for it exists mainly the in since the idea is to have an object in the stack (one struct and even more a ref struct that has more chance of being a large object) and wants to pass it to the called method without copying the object that is large. C# has recently given preference for allocating object to stack to reduce the pressure on Garbage Collector which makes the code slower and paused and so has abused some large objects in the stack what did not happen in the past. Do not worry about the in if you are not adopting the preferred allocation pattern in the stack, it is for more advanced programming that requires mastering various knowledge to do right (not that anyone can not use, but if not understand all commitments will make wrong use, why I say that can not choose what to learn, or learn everything or stay always on the surface).

The ref can be used for this but it does not give guarantees on its modification what often the guarantee is desired, and with this guarantee the compiler can do some optimizations. In part you can think of him as a in that allows you to change its value normally. In part it serves for another that I will quote below.

But the ref can also be used to effectively allow a change that would normally not be allowed. For example, if you pass a string as an argument for a method, it is a reference, and how string is immutable if you want to change something just by creating another string, But if you create another one, how are you going to tell the one you called that now she must have another object? They are usually independent things, and one of the ways is to return this new string and hope for those who called to keep in the same variable that was passed as argument, which will not always happen, passing with ref this is guaranteed, changed the reference of the string to a new string the variable used as argument will receive the change and point to the new string. So ref is even more used to ensure that any change within a method is reflected in the calling method.

Already the out was widely used for cases where you needed to return more than one value in a method, as it was possible to return only one you passed a reference to the method, without having anything of useful value and the method called took charge of putting a value there. It can always do otherwise, but it was done and more complicated, with the advent of tuples by value became easy and this mechanism should be much less used, unless to do some things on a lower level, how is the deconstruction of objects in tuples and interoperability with other languages like C for example.

Even there already exists the return ref that makes the out be less necessary.

Note that all are references, only have a slightly different guarantees.

If you’re not doing something lower-level, interoperable, or using an API that requires it or wants to perform more generally, you don’t need this.

The stack has always been more interesting to use, and so unlike Java, C# has always liked it more than the heap, now it’s liking more and needs to have these mechanisms, so C# gets closer to languages like C, C++, Rust, etc.

  • Useful for understanding heap and stack: https://answall.com/questions/3797/o-que-s%C3%A3o-e-onde-est%C3%A3o-o-stack-e-heap

Browser other questions tagged

You are not signed in. Login or sign up in order to post.