3

I am trying to refresh my understanding of C#. I have used Java before and passing by reference is a powerful tool that I truly miss in Java. However, I needed clarification if there is even a need to pass an object by reference and if there is a scenario where it may be helpful? Stackoverflow already has multiple questions around reference and they were truly enlightening, however, I was wondering if there is a scenario for passing objects by reference. Let me attach the code to better illustrate it.

class Program
    {
        public static void Update(int x) { x++; }
        public static void Update(ref int x) { x++; }
        public static void Update(Employee x) { x.msalary += 2000; }
        public static void Update(ref Employee x) { x.msalary += 2000; } //Do we even need this method? Is there a scenario where we might need this?

        static void Main(string[] args)
        {

            int a = 0;

            Update(a);
            Console.WriteLine("T1  " + a.ToString()); //a is still 0

            Update(ref a);
            Console.WriteLine("T2  " + a.ToString());//a is 1

            Employee emp2 = new Employee("Brent", "1234", "Blue", 1000); //salary is 1000

            Update(emp2.msalary);
            Console.WriteLine("T3  " + emp2.msalary.ToString());//salary does not change.

            Update(ref emp2.msalary);
            Console.WriteLine("T4  "+emp2.msalary.ToString());//salary changes to 1001

            Update(emp2);
            Console.WriteLine("T5  " + emp2.msalary.ToString()); //This changes, as expected for objects.

            Update(ref emp2);
            Console.WriteLine("T6  " + emp2.msalary.ToString()); //This also changes. But is there a scenario where we might need this?


        }
    }
baleeghr00
  • 37
  • 2
  • 1
    Your `Update(ref Employee x)` method can do `x = new Employee()`, and that change is reflected in the caller – canton7 Mar 21 '20 at 16:09
  • 4
    The scenario is the same for both reference and value types - when you need to assign to the variable in the called function and have that be visible in the caller. You aren't assigning `x` in your `ref Employee` example so it's not required. – Lee Mar 21 '20 at 16:10
  • I would say for a value type only if you want to change the value and have it reflected outside of the scopre. For a reference type only if you might make the current instance null or if you need to assign a new instance to the reference. – Jonathan Alfaro Mar 21 '20 at 16:10
  • 1
    In your case the reference to Employee is not being modified. You are only modifying the actual object but not the reference to it. – Jonathan Alfaro Mar 21 '20 at 16:11
  • 1
    why would the useful scenarios in C# be any different to Java, did you think? – ADyson Mar 21 '20 at 16:11
  • Notable how the highly voted comment is not correct. Try declaring Employee as a *struct* to see the difference. – Hans Passant Mar 21 '20 at 16:27
  • _"I was wondering if there is a scenario for passing objects by reference"_ -- that's not what `ref` is. As explained in numerous Q&A already, `ref` affects how the _parameter_ is passed, not the object the parameter may or may not refer to. – Peter Duniho Mar 21 '20 at 18:32
  • `ref` is often need in recursion. – Second Person Shooter Mar 21 '20 at 19:59

1 Answers1

7

Calling it "ref" was in my opinion a mistake; the right way to think of this feature is that it makes a local or parameter an alias to another variable. That is, when you use ref you have just given another name to an existing variable.

So the question then is: under what circumstances does it make sense to modify someone else's variable? Those are the circumstances in which you should use ref.

Back in historical times the main use case for ref was something like bool TryParse(string s, out int x) where you want to have two return values: a bool and an int. But that method was created in the C# 1 days before generics, nullables and tuples. The better practice now is: if you need to return a value type that could be invalid, return a nullable, and if you need to return two values, return a tuple. (Remember, out is just ref that requires writing before reading.)

What then is the current use case for ref in new code that uses tuples and nullable value types? There are some algorithms where you can gain a small amount of performance by reading and modifying a variable in another part of the data structure directly, but you need to pass around which variable needs reading and modifying. That is, ref should be used as a performance optimization for the implementation details of certain data types. (Remember that you cannot permanently store refs; you can only make a local an alias to another variable, and that local cannot have its lifetime extended! This greatly limits the use cases for refs.)

You can also use ref as a more clean, type-safe way to interoperate with unmanaged code that uses pointers as aliases for variables.

That's about it. I almost never use ref in mainstream, line-of-business code. It's there for when you need it, but you almost never do.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thank you for the clarification Eric. I have coded in C before, and ref reminded me of the pointer functionality in C. I thought that using ref might enhance code performance, but based on everyone's feedback, I will avoid using it. – baleeghr00 Mar 21 '20 at 16:29
  • @baleeghr00: IMHO, C language has no "passing by variable sharing" feature above, but C++ does. – Second Person Shooter Mar 21 '20 at 18:55
  • 1
    What are pointers if not aliases to variables? – Eric Lippert Mar 21 '20 at 19:21
  • A variable associated with a memory storage location of type pointer in C/C++ contains the address (aka reference in C#) of an object. It is the same as a variable of reference type in C#. An alias shares the same memory storage location. – Second Person Shooter Mar 21 '20 at 19:49
  • `p` in `int* p;` is a pointer and `q` in `int*& q = p;` is an alias of `p`. – Second Person Shooter Mar 21 '20 at 20:02
  • 2
    Sure, but what I'm getting at is that if we have `int x = 123;` and `int *p = &x;` then `*p` is also a variable and moreover is an alias for `x`. We don't need C++ references; they are just a pleasant sugar over pointers. – Eric Lippert Mar 21 '20 at 22:27
  • `void Do(int** alias){int y = 10; *alias = &y;} int main(){int x = 1; int* pointer = &x; Do(&pointer); std::cout << *pointer; }` – Second Person Shooter Mar 22 '20 at 10:42
  • That right there is dangerously undefined behaviour. You'll note that the same program is impossible in C# using managed references; if you use `unsafe` then you are responsible for not doing stuff like this. – Eric Lippert Mar 22 '20 at 17:36
  • And of course you can write a much simpler version of this bad program: `int* bad() { int y = 1; return &y;} ...` It's always dangerous to make a pointer to a variable where the lifetime of the pointer is longer than the lifetime of the variable; C# has a variety of techniques for preventing that with `ref` locals. – Eric Lippert Mar 22 '20 at 17:41
  • What about `Try-` methods that return reference types that can be null? How would you define "today" an `INewDictionary` interface, without a `TryGetValue` method? – Luca Cremonesi Mar 27 '20 at 19:58