views:

602

answers:

7

Here is what I understand so far:

PASS BY VALUE

Passing by value means a copy of an argument is passed. Changes to that copy do not change the original.

PASS BY REFERENCE

Passing by reference means a reference to the original is passed. changes to the reference affect the original.

REF Keyword

REF tells the compiler that the object is initialized before entering the function. REF means the value is already set, the method can therefore read it and modify it. REF is two ways, both in and out.

OUT Keyword

OUT tells the compiler that the object will be intialized inside the function. OUT means the value is not already set, and therefore must be set before calling return. OUT is only one way, which is out.

Question

So in what scenarios would you combine the use of the ref and out keywords, with passing by reference or passing by value? Examples would help tremendously.

Help greatly appreciated.

+9  A: 

You would never combine ref and out on 1 parameter. They both mean 'pass by reference'.

You can of course combine ref parameters and out parameters in one method.

The difference between ref and out lies mainly in intent. ref signals 2-way data transport, out means 1-way.

But besides intent, the C# compiler tracks definite-assignment and that makes the most noticable difference. It also prevents the misuse (reading) of an out parameter.

void SetOne(out int x) 
{
  int y = x + 1; // error, 'x' not definitely assigned.
  x = 1;
}

void AddTwo(ref int x)
{
    x = x + 2;  // OK, x  is known to be assigned
}

void main()
{
    int foo, bar;

    SetOne(out foo); // OK, foo does not have to be assigned
    AddTwo(ref foo); // OK, foo assigned by SetOne
    AddTwo(ref bar); // error, bar is unassigned
}
Henk Holterman
Ray, in my examples `AddTwo(out foo)` will modify `foo`. That's the by-ref part. How it works is implementation-dependent but it will usually be the same for ref and out. And probably stack-offsets instead of pointers.
Henk Holterman
I'm sorry. Yes, there was a bug in my test code. After correcting it I saw that 'out' parameters are not passed by OUT but are in fae passed by reference with the additional constraint of definite assignment. Please accept my apologies.
Ray Burns
A: 

You pass by ref something you want to be read and written by another function, so you should pass the variable initialized in order to be correctly read.

EDIT: example: void mymethod(ref int a) { a++; }

You pass as OUT something you want to be written by another function, but you don't need to initialize it as it won't be read by the function, only written.

EDIT: example:

void mymethod2(out string a) { a="hello"; }

rossoft
+1  A: 

Edit: I have corrected this answer to reflect that the 'out' keyword in C# does not do what you might expect it to (eg a true OUT parameter in the computer science sense of the term). I originally stated that 'out' was pass by value OUT but was proven wrong.

You can't use 'out' and 'ref' together. There are three calling conventions in C# (NET Framework):

  • No keyword = Pass by value (IN)
  • 'out' Keyword = Pass by reference (REF) with no definite assignment requirement before call
  • 'ref' Keyword = Pass by reference (REF) with a definite assignment requirement before call

C# has no true OUT or IN-OUT parameter ability.

To see that 'out' parameters in C# are not true OUT parameters, you can use the following code:

  public class Test
  {
    Action _showValue;

    public void Run()
    {
      string local = "Initial";
      _showValue = () => { Console.WriteLine(local.ToString()); };

      Console.WriteLine("Passing by value");
      inMethod(local);

      Console.WriteLine("Passing by reference with 'out' keyword");
      outMethod(out local);

      Console.WriteLine("Passing by reference with 'ref' keyword");
      refMethod(ref local);

    }

    void inMethod(string arg)
    {
      _showValue();
      arg = "IN";
      _showValue();
    }

    void outMethod(out string arg)
    {
      _showValue();
      arg = "OUT";
      _showValue();
    }

    void refMethod(ref string arg)
    {
      _showValue();
      arg = "REF";
      _showValue();
    }
  }

The output is:

Passing by value
Initial
Initial
Passing by reference with 'out' keyword
Initial
OUT
Passing by reference with 'ref' keyword
OUT
REF

As you can see, both 'out' and 'ref' actually pass by REF. The only difference is in how the compiler treats them for definite assignment purposes.

Ray Burns
`out` is **not** passing by value.
Mehrdad Afshari
It depends on what computer science textbook you are reading, but most consider IN, OUT, and IN-OUT all examples of "pass by value". The essential concept of passing by value is that when it is passed you copy the value instead of a pointer to it. BTW, there are five recognized parameter passing schemes in all: IN, OUT, IN-OUT, REF, and NAME. C# only has IN, OUT, and REF. Passing by NAME is very interesting. Look up Algol-68 if you are curious.
Ray Burns
I forgot that many backtracking languages can also pass OUT by reference. This makes a difference when they return multiple times, for example with a Scheme call/cc or any language containing true generators.
Ray Burns
My apologies. I was incorrect about the C# usage of the 'out' keyword. As Mehrdad, Henk, and others have said, it does not actually create an OUT parameter but a REF parameter with additional constraint on definite assignment. Sorry for the confusion. My first comment is accurate, though. I will edit my answer to avoid future confusion.
Ray Burns
No worries; it is easy to get this stuff confused. Just to be clear, both out and ref impose definite assignment requirements. (1) On the caller side (1a) ref variables must be definitely assigned before the call (1b) out variables need not be definitely assigned before the call, (1c) out variables are DA after the call. (2) on the callee side, (2a) ref variables are DA throughout, (2b) out variables are not-DA upon entry, and (2c) out variables must be DA before every normal return.
Eric Lippert
Yes, my text was misleading. I did undertand that part of it clearly but the way I said it was not clear. I have corrected my verbiage.
Ray Burns
To distinguish C#/COM-style ref/out from your in/out/in-out/ref, what I usually do is I say "copy-out" or "copy-in-copy-out" when you would just say "out" or "in-out".
Eric Lippert
Also, I note that there are other parameter passing mechanisms other than just your five. Pass-by-lazy-evaluation, for example, immediately comes to mind. (And I agree, Algol 68's pass-by-name is a bit weird.)
Eric Lippert
A: 

Using the OUT keyword is useful if you have a method that you need to return more than one value. For example, look at methods like int.TryParse().

Using REF is more for explicitness of objects. Keeping in mind that any non-primitive passed into a method is inherently passed by reference there's not a lot of need for it in normal managed code. By declaring the REF keyword you're stating that the parameter will likely be modified in the body of the method and thus the calling code should be aware of it (thus why you have to explicitly add the ref in the calling code as well.

Agent_9191
A: 

You understand the dynamics of passing either way. Some parameter scenarios might be:

  • ref int num for an in/out parameter. The function may modify the value in it.
  • out int num like ref except function must assign a value to it before returning.

In general output parameters are good for when a function must return multiple values, because a function only has one return value (although it can be compound).

Sometimes data access providers, like some ADO.NET methods, use output parameters to deliver information back. Some data access methods mimic database stored procedures which have in/out parameters.

Edit: One stipulation for reference types is the parameters ref StringBuilder word or StringBuilder word (by value) behave the same - the string outside is affected, although the underlying implementatoin may differ slightly because at that point the focus is the reference and not the value on the heap.

John K
A: 

If you understand c++ maybe this will help you:

void Foo(Bar) {} // pass by value c# (if it's a value type ;))
void Foo(Bar) {} // c++

void Foo(Bar) {} // pass by reference c# (if it's a reference type ;))
void Foo(Bar&) {} // c++

void Foo(ref Bar) {} // c#
void Foo(Bar*) // c++

void Foo(out Bar) {} // c#
void Foo(Bar**) {} // c++ (the contents of *Bar needs to be filled up)
Stormenet
+7  A: 

Help greatly appreciated

Your understanding will be improved by a correct and careful use of language.

Passing by value means a copy of an argument is passed.

Yes, this is exactly accurate.

Changes to that copy do not change the original.

Not exactly. Begin by carefully distinguishing between values and variables. Consider:

class Foo { public int x; }
...
void N() 
{
  Foo blah = new Foo();
  blah.x = 0;
  M(blah);
}
...
void M(Foo foo)
{
  foo.x = 123; // changes blah.x
  foo = null; // does not change blah
}

The variables here are x, blah and foo. x is a field, blah is a local, foo is a formal parameter.

The values here are null, 0, 123, and a reference to an instance of Foo. That reference is a value. It is crucial to understand this fact.

A copy of the value of blah is passed by copying the value of variable blah into variable foo. The value of blah is a reference to the instance of Foo.

M can change the value of variable x because M has a copy of the value of blah, which is a reference to a Foo. When M changes the contents of foo to null, that does not change blah; foo contains a copy of the value of blah.

Passing by reference means a reference to the original is passed.

Choose your wording carefully. What is "the original"?

This would be better stated as "passing by reference means that a reference to the argument, which must be a variable, is passed". An easier way to think about it is that "passing by reference makes the parameter an alias for the variable passed as the argument".

changes to the reference affect the original.

Since the parameter and the argument are aliases for each other, it's not that changes to the reference affect the original; the reference IS the original. They are both the same variable.

REF tells the compiler that the object is initialized before entering the function.

"The object" is meaningless. You mean "the variable".

"ref" does not "tell the compiler that the variable is initialized". Rather, "ref" tells the compiler "you, compiler, must verify that the variable is initialized". That is rather different!

REF means the value is already set,

No, ref requires that the variable is already set. There's no such thing as "setting a value".

the method can therefore read it and modify it.

Where by "it" you mean "the variable".

REF is two ways, both in and out.

Correct.

OUT tells the compiler that the object will be intialized inside the function.

Stop using "the object" to mean "the variable". You will understand things much more clearly if you stop confusing completely different things. Variables are not objects. Variables are storage locations some of which might contain values, and some of those values might be references to objects.

So, out tells the compiler that the variable will be initialized inside the method, yes, but that's not quite right. You are forgetting about the cases where the method will throw an exception, or the method will go into an infinite loop -- those are also legal scenarios.

OUT means the value is not already set,

Again, by "the value", you mean "the variable". But this is not accurate. It is perfectly legal to pass an initialized variable as an "out" parameter. Pointless, but legal.

and therefore must be set before calling return.

"return" is not called; methods are called. But yes, the method must assign a value to the variable before returning normally.

OUT is only one way, which is out.

Right.

So in what scenarios would you combine the use of the ref and out keywords

There are no such scenarios.

Eric Lippert