325

In C#, I have always thought that non-primitive variables were passed by reference and primitive values passed by value.

So when passing to a method any non-primitive object, anything done to the object in the method would effect the object being passed. (C# 101 stuff)

However, I have noticed that when I pass a System.Drawing.Image object, that this does not seem to be the case? If I pass a system.drawing.image object to another method, and load an image onto that object, then let that method go out of scope and go back to the calling method, that image is not loaded on the original object?

Why is this?

nawfal
  • 70,104
  • 56
  • 326
  • 368
Michael
  • 8,229
  • 20
  • 61
  • 113
  • 27
    All variables are passed by value by default in C#. You are passing *the value of the reference* in the case of reference types. – Andrew Barber Jan 03 '12 at 06:22
  • Same thing: [why-is-list-when-passed-without-ref-to-a-function-acting-like-passed-with-ref](http://stackoverflow.com/questions/7321602/why-is-list-when-passed-without-ref-to-a-function-acting-like-passed-with-ref) – nawfal Oct 13 '16 at 06:01
  • Since there was no code given, it is not really clear what is being asked. Maybe the OP meant `image.Load(filename)` or maybe they meant `image = Image.Load(filename)` where `image` is the function param. – StayOnTarget Apr 12 '21 at 19:53

9 Answers9

663

Objects aren't passed at all. By default, the argument is evaluated and its value is passed, by value, as the initial value of the parameter of the method you're calling. Now the important point is that the value is a reference for reference types - a way of getting to an object (or null). Changes to that object will be visible from the caller. However, changing the value of the parameter to refer to a different object will not be visible when you're using pass by value, which is the default for all types.

If you want to use pass-by-reference, you must use out or ref, whether the parameter type is a value type or a reference type. In that case, effectively the variable itself is passed by reference, so the parameter uses the same storage location as the argument - and changes to the parameter itself are seen by the caller.

So:

public void Foo(Image image)
{
    // This change won't be seen by the caller: it's changing the value
    // of the parameter.
    image = Image.FromStream(...);
}

public void Foo(ref Image image)
{
    // This change *will* be seen by the caller: it's changing the value
    // of the parameter, but we're using pass by reference
    image = Image.FromStream(...);
}

public void Foo(Image image)
{
    // This change *will* be seen by the caller: it's changing the data
    // within the object that the parameter value refers to.
    image.RotateFlip(...);
}

I have an article which goes into a lot more detail in this. Basically, "pass by reference" doesn't mean what you think it means.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    Your right, I didnt see that! I loading image = Image.FromFile(..) and that was replacing the variable image and not changing the object! :) of course. – Michael Jan 03 '12 at 06:33
  • in simple words can i say if we change the properties or call some function of the parameter object it will be affected but if we initiate the parameter variable then it will be a ref to new location/object. paramX.Caption = "asdasdasd"; //will work paramX = new Object(); //will disconnect it from the caller and that paramX will be ref to new location – Adeem Feb 27 '15 at 11:39
  • 1
    @Adeem: Not quite - there's no "parameter object", there's the object that the value of the parameter refers to. I think you've got the right idea, but terminology matters :) – Jon Skeet Feb 27 '15 at 11:50
  • 2
    If we drop keywords `ref` and `out` from c#, is it ok to say that c# passes parameters the same way as java does i.e. always by value. Is there any difference with java. – broadband Jun 03 '15 at 07:15
  • 1
    @broadband: Yes, the default passing mode is by-value. Although of course C# has pointers and custom value types, which makes it all a bit more complicated than in Java. – Jon Skeet Jun 03 '15 at 07:17
  • So calling `myobj.Value = null` will be seen by the caller because I changing its internal data. – Jax Aug 16 '16 at 19:25
  • 2
    @DJMethaneMan: Yes, exactly. That's not changing the parameter (assuming `myobj` is of some reference type...) – Jon Skeet Aug 16 '16 at 19:28
  • So does this mean that the object being passed without ref or out (default) is "essentially" a copy of that object? – Vippy Aug 19 '16 at 18:38
  • 5
    @Vippy: No, not at all. It's a copy of the *reference*. I suggest you read the linked article. – Jon Skeet Aug 19 '16 at 18:52
  • You forgot to use `out` keyword in the last function. – Sujoy May 21 '19 at 05:19
  • @Sujoy: No I didn't - as per the comment, "it's changing the data within the object that the parameter value refers to". It doesn't change the value of the parameter itself, so it would be pointless for it to be an `out` parameter. – Jon Skeet May 21 '19 at 08:12
  • 3
    aaaah, examples 1 & 3 pass a copy of the pointer (address) to the source reference type so initially both the source and local things are looking in the same place at the same object but example one changes the pointer of the local copy inside the method so now that is looking at something else, the original pointer is untouched because it can't be touched. Example 2 (ref) uses the same pointer for both so if you mess with it, it changes both. – CAD bloke May 06 '20 at 23:35
  • Wow! I had not thought of this take on byref or byval. By default, dotnet does not quite use either for objects, it uses a _copy of the ref_ , or a new pointer to the old pointer. If a new instance is created, the pointer to that instance must be placed in the memory address created for the input parameter. Many thanks for this insight @JonSkeet – martinp999 Jul 01 '21 at 23:14
  • 1
    @martinp999: "copy of the ref" is precisely pass by value; a copy of the existing value (which is a reference) is passed, just like all normal pass by value semantics. I don't know what you mean by "the pointer to that instance must be placed in the memory address created for the input parameter" - what do you mean by "must" here? (I'd tend to avoid using the term "pointer" where possible when talking about C#, unless you're actually referring to a C# pointer.) – Jon Skeet Jul 02 '21 at 02:19
103

Lots of good answers had been added. I still want to contribute, might be it will clarify slightly more.

When you pass an instance as an argument to the method it passes the copy of the instance. Now, if the instance you pass is a value type(resides in the stack) you pass the copy of that value, so if you modify it, it won't be reflected in the caller. If the instance is a reference type you pass the copy of the reference(again resides in the stack) to the object. So you got two references to the same object. Both of them can modify the object. But if within the method body, you instantiate new object your copy of the reference will no longer refer to the original object, it will refer to the new object you just created. So you will end up having 2 references and 2 objects.

OlegI
  • 5,472
  • 4
  • 23
  • 31
23

One more code sample to showcase this:

void Main()
{


    int k = 0;
    TestPlain(k);
    Console.WriteLine("TestPlain:" + k);

    TestRef(ref k);
    Console.WriteLine("TestRef:" + k);

    string t = "test";

    TestObjPlain(t);
    Console.WriteLine("TestObjPlain:" +t);

    TestObjRef(ref t);
    Console.WriteLine("TestObjRef:" + t);
}

public static void TestPlain(int i)
{
    i = 5;
}

public static void TestRef(ref int i)
{
    i = 5;
}

public static void TestObjPlain(string s)
{
    s = "TestObjPlain";
}

public static void TestObjRef(ref string s)
{
    s = "TestObjRef";
}

And the output:

TestPlain:0

TestRef:5

TestObjPlain:test

TestObjRef:TestObjRef

MatthewT
  • 638
  • 6
  • 17
vmg
  • 9,920
  • 13
  • 61
  • 90
  • 4
    So basically reference type still NEED TO BE PASSED as reference if we want to see the changes in the Caller function. – Unbreakable Jun 02 '18 at 22:20
  • 7
    Strings are immutable reference types. Immutable means, it cannot be changed after it has been created. Every change to a string will create a new string. That's why strings needed to be passed as 'ref' to get change in calling method. Other objects (e.g. employee) can be passed without 'ref' to get the changes back in calling method. – Himalaya Garg Feb 08 '20 at 04:00
  • 4
    @vmg, as per HimalayaGarg, this isn't a very good example. You need to include another reference type example which isn't immutable. – Daniel Mar 03 '20 at 22:48
17

I guess its clearer when you do it like this. I recommend downloading LinqPad to test things like this.

void Main()
{
    var Person = new Person(){FirstName = "Egli", LastName = "Becerra"};

    //Will update egli
    WontUpdate(Person);
    Console.WriteLine("WontUpdate");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateImplicitly(Person);
    Console.WriteLine("UpdateImplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateExplicitly(ref Person);
    Console.WriteLine("UpdateExplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
}

//Class to test
public class Person{
    public string FirstName {get; set;}
    public string LastName {get; set;}

    public string printName(){
        return $"First name: {FirstName} Last name:{LastName}";
    }
}

public static void WontUpdate(Person p)
{
    //New instance does jack...
    var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName};
    newP.FirstName = "Favio";
    newP.LastName = "Becerra";
}

public static void UpdateImplicitly(Person p)
{
    //Passing by reference implicitly
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

public static void UpdateExplicitly(ref Person p)
{
    //Again passing by reference explicitly (reduntant)
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

And that should output

WontUpdate

First name: Egli, Last name: Becerra

UpdateImplicitly

First name: Favio, Last name: Becerra

UpdateExplicitly

First name: Favio, Last name: Becerra

Adriano Carneiro
  • 57,693
  • 12
  • 90
  • 123
Egli Becerra
  • 961
  • 1
  • 13
  • 25
7

When you pass the the System.Drawing.Image type object to a method you are actually passing a copy of reference to that object.

So if inside that method you are loading a new image you are loading using new/copied reference. You are not making change in original.

YourMethod(System.Drawing.Image image)
{
    //now this image is a new reference
    //if you load a new image 
    image = new Image()..
    //you are not changing the original reference you are just changing the copy of original reference
}
Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
Haris Hasan
  • 29,856
  • 10
  • 92
  • 122
3

How did you pass object to method?

Are you doing new inside that method for object? If so, you have to use ref in method.

Following link give you better idea.

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
dotnetstep
  • 17,065
  • 5
  • 54
  • 72
2
Employee e = new Employee();
e.Name = "Mayur";

//Passes the reference as value. Parameters passed by value(default).
e.ReferenceParameter(e);

Console.WriteLine(e.Name); // It will print "Shiv"

 class Employee {

   public string Name { get; set; }

   public void ReferenceParameter(Employee emp) {

     //Original reference value updated.
    emp.Name = "Shiv";

    // New reference created so emp object at calling method will not be updated for below changes.
    emp = new Employee();
    emp.Name = "Max";
  }
}
Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Mayur Pawar
  • 107
  • 2
  • 16
-1

In Pass By Reference You only add "ref" in the function parameters and one more thing you should be declaring function "static" because of main is static(#public void main(String[] args))!

namespace preparation
{
  public  class Program
    {
      public static void swap(ref int lhs,ref int rhs)
      {
          int temp = lhs;
          lhs = rhs;
          rhs = temp;
      }
          static void Main(string[] args)
        {
            int a = 10;
            int b = 80;

  Console.WriteLine("a is before sort " + a);
            Console.WriteLine("b is before sort " + b);
            swap(ref a, ref b);
            Console.WriteLine("");
            Console.WriteLine("a is after sort " + a);
            Console.WriteLine("b is after sort " + b);  
        }
    }
}
Shree Krishna
  • 8,474
  • 6
  • 40
  • 68
-1

In the latest version of C#, which is C# 9 at this time of writing, objects are by default passed by ref. So any changes made to the object in the calling function will persist in the object in the called function.

Sujoy
  • 1,051
  • 13
  • 26
  • 1
    this does not seem to be the case for me... – XRaycat Oct 15 '21 at 21:07
  • 1
    What is your source for this? [Documentation](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-parameters) published this month doesn't mention that. Nor does the [documentation](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-reference-type-parameters) for passing reference types. – ProgrammingLlama Jan 17 '22 at 04:09
  • 1
    @ProgrammingLlama Indeed, and it seems like this would break a lot of existing code if it were just changed like that after 8 previous versions. – Michael Mar 15 '23 at 22:03