-2

There are many examples, let's take array copy method as an example. The signature of the Array.Copy is method is as below

public static void Copy (Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length);

Judging only from signature, one can not tell that the sourceArray will not be changed while the destinationArray will be altered, even if it is some thing as simple as an array of Int. The guarantee coming from the keyword "ref" for programmers have lost here.

It seems to me that the the destinationArray parameter should better be marked as "ref Array". If it had been done this way, the syntax would be more consistent with the usage of the keyword "ref", indicating that the passed in object might be modified by the callee and the change is visible for the caller. The only benefit I can think of concerning mitting the keyword "ref", is that saves a few key strokes. or it is just mimicking the C/C++ style without much thinking.

My question is: what are some seasonings behind this design decision?

Update: For the record, I am advocating that an array be of the same value/reference category as its elements, thus making a clear extinction between Fun(array) and Fun(ref array), that is the same guarantee programmers get with Fun(int) and Fun(ref int). Optimization for efficiency can be left to the implementation level.

John Z. Li
  • 1,893
  • 2
  • 12
  • 19

3 Answers3

4

Array is a reference type. You can pass references by value and the instances they reference will still be the same ones that get modified. The callee is modifying the same instance using its own reference to it and has no reason to change it into a completely different instance entirely (which is where ref would actually come into use).

There isn't any convention that states to use ref when passing reference types — you generally don't need to most of the time, except as mentioned if your method actually intends to change the instance entirely like so:

class Foo { public int Value; }

public static void ReplaceFoo(ref Foo foo)
{
    foo = new Foo { Value = 2 };
}

var foo = new Foo { Value = 1 };
Console.WriteLine(foo.Value);
ReplaceFoo(ref foo);
Console.WriteLine(foo.Value);

Judging only from signature, one can not tell that the sourceArray will not be changed while the destinationArray will be altered

Why is this a problem? No one reads APIs only paying attention to method signatures and ignoring parameter names. Signatures are there for the compiler to distinguish overloads. Anyone reading the API for Array.Copy() would understand that sourceArray is going to be unchanged, being where the method is getting the values from, and destinationArray is going to be modified, being the one receiving the values — unless they don't speak English (which is fine, but most APIs are written in English).

The only other scenario I can think of where a reader would be confused is if they didn't have the prior knowledge that arrays are reference types in .NET. But misusing ref in a situation where it's not needed at best and inappropriate at worst doesn't solve that problem.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • Quote from MSDN ". You can also use the ref keyword to pass reference types by reference. Passing a reference type by reference enables the called method to replace the object to which the reference parameter refers in the caller. The storage location of the object is passed to the method as the value of the reference parameter. If you change the value in the storage location of the parameter (to point to a new object), you also change the storage location to which the caller refers. The following example passes an instance of a reference type as a ref parameter." – John Z. Li Oct 03 '18 at 02:56
  • 3
    @John Z. Li: Yes, you *can* use ref with reference types, but its usage is so rare I would be hard-pressed to call it a convention. Read what you just quoted again and ask yourself, when do you actually need to do that? How does that use case apply to Array.Copy()? – BoltClock Oct 03 '18 at 02:58
  • @John Note that you're not replacing the array in the method in your example, you're simply changing the values held by it. AFAIK The only real need to use `ref` for reference types is when the method will potentially replace the object with a new one. – ProgrammingLlama Oct 03 '18 at 03:00
  • To be consistent, ref keyword should be applied to each element of an array, and lacking it should act in a way as if each element is passed in without it, IMHO. – John Z. Li Oct 03 '18 at 03:02
  • 2
    @John Z. Li: That sounds very unusual and not at all consistent with *anything*, not even what you've quoted. Plus, given that you only have a reference to the array itself to work with, and you aren't going to know how many elements it has in advance, how is that going to work? – BoltClock Oct 03 '18 at 03:04
  • @BoltClock Consider the following combination: 1, array passed by value with its elements passed by value, 2, array passed by value with its elements passed by reference, 3, array passed by reference with its elements passed by value, 4, array passed by reference with its elements passed by reference. – John Z. Li Oct 03 '18 at 03:09
0

C# (and .NET) include both reference types and value types.

Normally (absent ref or out keywords), parameters are passed to methods by value. So, if you pass an integer to a function, the value of the integer is passed. If you put a a variable referring to an array in a function call (remembering that all arrays are instances of the reference type System.Array), the value of that variable, i.e., the reference to to the array, is passed to the function.

So, within the function, the code gets to play on that array. When the function returns, that variable (in the scope of the caller) still refers to that same object. However, the function may have mutated that array, so the variable (in the caller scope) may be referring to a changed object.

If you pass a value type by reference (with the ref keyword), the function can change the value of the parameter, and when the function returns, the variable (in the caller scope) will receive the new value.

But, if you use ref (or out) on a parameter of reference type, you are passing a reference by reference. So, for example, you could pass in an array of five integers and the function could assign that parameter and array of ten integers (they are of the same type, but definitely differentobjects). In the caller, when the function returns, the variable associated with that parameter will see what it refers to completely change during the call.

In your example, the caller will instantiate two arrays of the same type and compatible lengths (usually the same length if the source and destination indexes are 0 and the length is sourceArray.Length). The function does not change what object the destination array parameter refers to, it just fills the destination from the source.

In fact, if the destination was by ref, it wouldn't be as flexible. Consider a case where the destination is 30 entries long, and your intention is to fill the middle ten array entries with the source. It just works. It wouldn't with a ref destination parameter (without a lot more work).

Flydog57
  • 6,851
  • 2
  • 17
  • 18
  • @vasily.sib: Their references are passed by value. This results in two distinct references to one instance. Nothing is passed by reference by default - you have to explicitly do so using the ref keyword. – BoltClock Oct 03 '18 at 03:42
  • @BoltClock, I see this now. Sorry about that. – vasily.sib Oct 03 '18 at 03:51
-1

The reason for omitting the ref keyword is that in most cases, it won't make any difference to include it, so it's superfluous. However, it does actually make a difference in some cases. An array is a reference type, and that means a value representing that reference gets passed. Normally, updating the passed in value will trigger updates to the original object. BUT if you create a NEW array and assign the passed in parameter to the new item, the reference gets lost - whereas the ref keyword preserves it.

nycdanielp
  • 187
  • 1
  • 2
  • It dies make a big difference. Passing a reference by reference is very different than passing it by value. – Flydog57 Oct 03 '18 at 05:00
  • Of course it does, but not for reference types. That's the whole point of why the ref keyword doesn't usually for a difference for those types. – nycdanielp Oct 03 '18 at 12:15