开发者

How do you make C# function parameters act as values?

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 h开发者_开发知识库ere
}


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.


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).


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.


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.


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.


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

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


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

UPD Link to download C# 4.0 specs.


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).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜