views:

229

answers:

8

Hello,

My question is related to C# function parameters. I'm used to C++ where parameters are values by default unless you explicitly specify them as references. So, in C#, how do I make function parameters to be passed by value instead of by reference? Actually, I just want to know how to pass a variable, and not have that variable modified after the call to the function.

E.g:

void foo(Widget w)
{
     w.X = 3;//where w wouldn't really be modified here
}
+3  A: 

As int is a primitive data type, x is already passed by value in your older example.

Proof of concept:

class Program
{
    static void Main(string[] args)
    {
        int x = 1;
        System.Console.WriteLine(x); // 1

        foo(x);
        System.Console.WriteLine(x); // 1
    }

    static void foo(int x)
    {
        x++;
    }
}

EDIT: for non-primitive data types, the answers to this question state that C# doesn't implement copy constructors like C++ does, so I'm not sure of a way to pass actual objects by value, other than making your class implement the ICloneable interface or something. (For the record, C# passes references to objects by value.)

EDIT 2: Mark Rushakoff's answer is an excellent, if not the best, way to go... assuming your Widget type is defined by you (it looks so).

BoltClock
Oh, thank you. I'll change that.
Jesse Emond
@BoltClock, nice job finding the other question
Blair Conrad
A: 

Follow the link to find out the comprehensive explanation of Methods Parameters

UPD Link to download C# 4.0 specs.

Eugene Cheverda
-1 for posting a .NET 1.1 link!
John Saunders
This is the only provided MSDN Online Link with C# specs, other links require specs should be downloaded.
Eugene Cheverda
+1  A: 

All basic types are passed by value automatically. To pass them by reference:

void foo(ref int x) {
  x = 3; // x does get modified here
}

void bar(int x) {
  x = 3; // does nothing
}

It is the same for strings as well.

However, if you have a none primitive datatype, the reference to it is passed automatically. If the ref keyword is used in this case, it is possible to make the reference refer to another object.

If you want to modify an object without changing it, you will have to clone it. Some objects let you do it like this:

Foo f = new Foo(oldF);

And if it supports IClonable, you can do it like this:

Foo f = oldF.Clone();

Otherwise you will have to do it manually, e.g. with a Rectangle:

Rectangle r = new Rectangle(oldR.Location, oldR.Size);

Structs are automatically passed by value.

If you make your own class, and want to pass copies of it, you should implement IClonable, or make a copy constructor.

Vincent McNabb
But how could I make the none primitive datatype be passed by a value instead? Or at least just to be able to modify it inside a function without really changing it?
Jesse Emond
A: 

Use out or ref in your method parameter.

void foo(out Widget w)
{
     w.X = 3;//where w wouldn't really be modified here
}

void foo(ref Widget w)
{
     w.X = 3;//where w wouldn't really be modified here
}

Using ref requires the object to be initalised before you call your method (ie, Widget can't be null).

Damien Dennehy
XGhost27X is asking about doing the opposite of using out and ref...
Blair Conrad
A: 

If passing a reference type then you need to clone your object.

http://www.csharp411.com/c-object-clone-wars/

Chris Diver
+10  A: 

Nobody has mentioned that structs are passed by value:

struct Foo
{
    public int X;
    public override string ToString()
    {
        return "Foo.X == " + X.ToString();
    }
}

class Program
{
    static void ModifyFoo(Foo foo)
    {
        foo.X = 5;
        System.Console.WriteLine(foo);
    }

    static void Main()
    {
        Foo foo = new Foo();
        foo.X = 123;
        ModifyFoo(foo);
        System.Console.WriteLine(foo);
    }
}

Output:

$ mono ./a.exe
Foo.X == 5
Foo.X == 123

If you use structs for your types, then you can only modify the instance when you explicitly use ref or out in your methods... but obviously this only works when you have control over the types involved.

Mark Rushakoff
+3  A: 

To make code like this work in C++ you typically must provide a copy constructor for the Widget class. Copying objects in C++ is pretty common, it is often required if you store objects in a collection. It is however a source of horrid heap corruption bugs, the default copy constructor implemented by the compiler isn't always appropriate. It aliases pointers, copying the pointer value instead of the pointed-to value. A shallow copy instead of a deep copy. That blows up badly when the destructor of the class deletes the pointed-to object, like it should, leaving the pointer in the copy pointing to garbage.

The C# language doesn't have this behavior, it does not implement a copy constructor. It is very rarely required anyway, the garbage collector avoids the need to make copies. Very notable in any benchmark on collection classes.

You certainly can create your own copy constructor, you just have to implement it explicitly:

void Widget(Widget other) {
   this.X = other.X;
   // etc...
}

And create the copy explicitly as well:

Widget w = new Widget();
w.X = 42;
// etc...
foo(new Widget(w));

Which perhaps also makes it clearer that there's a non-zero cost to making those copies. If you want a widget to have value behavior automatically then make it a struct, not a class. When passed without the "ref" keyboard, the runtime now automatically makes the copy. It does a memberwise copy. It is shallow, just like the default C++ copy constructor. But without the pointer aliasing problem, the garbage collector solves that.

But with the shallow copy problem. Which most C# programmers tackle with "I tried that before, didn't work out well. Let's not do that again". Good advice, you shouldn't do that in C++ code either. It's expensive and error prone and you better know what you're doing. Maybe that's trivializing the problem a bit too much. Sorry.

Hans Passant
You can also do the copy inside `foo` of course, in which case it will never modify the objects passed in.
Ben Voigt
Garbage collection solves the "double free" problem caused by shallow copies. But that's far from the only effect of pointer aliasing.
Ben Voigt
+3  A: 

You can make the object immutable, so that mutations in functions are invisible unless you return the object back to the user. So you end up with something like this:

public class Widget
{
    public readonly string X;
    public readonly string Y;
    public readonly string Z;

    public Widget() { }
    public Widget(string x, string y, string z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public Widget SetX(string value) { return new Widget(value, y, z); }
    public Widget SetY(string value) { return new Widget(x, value, z); }
    public Widget SetZ(string value) { return new Widget(x, y, value); }
}

So now you can use it as such:

public void DoStuff(Widget w)
{
    w = w.SetX("hello");
    w = w.SetY("world");
}

Whatever you pass into the function is unaffected, because local mutations simply create a new object.

Juliet