Pass by Reference as "essential" to swap method exposed as a lie

R

raylopez99

In the otherwise excellent book C# 3.0 in a Nutshell by Albahari et
al. (3rd edition) (highly recommended--it's packed with information,
and is a desktop reference book) the following statement is made: (p.
39: "The ref modifier is essential in implementing a swap method")

This is untrue. The following program demonstrates it. See how
method "func4Swap" does a swap of two int variables without using ref,
but using a 'helper' class and using only pass by value

However, ref is important when using 'new' in the method calling the
variables to be used (not shown here), or, if you want to permanently
change the variables passed from inside the class doing the passing
(see func1 method and swap1 method below). In fact, I'm not sure you
can do a swap without using ref and without using a 'helper' class as
in the below.

RL

// OUTPUT AT END

// Pass by reference, Pass by Value demonstrated // October 15, 2008

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;


namespace console1
{
class Program
{
static void Main(string[] args)
{
////////////////////////////////////////////
// show pass by ref

PassByRef01 myPBRclas1 = new PassByRef01();

Console.WriteLine("I. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k,myPBRclas1.m);
myPBRclas1.func2(myPBRclas1.i, myPBRclas1.j);
Console.WriteLine("II. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);
myPBRclas1.func1(ref myPBRclas1.k, ref myPBRclas1.m);
Console.WriteLine("III. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);

PassByRef01 placeHolderPassByRef01cl = new PassByRef01();

placeHolderPassByRef01cl.func3(myPBRclas1);
Console.WriteLine("IV. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);

placeHolderPassByRef01cl.func2(myPBRclas1.k, myPBRclas1.m);
Console.WriteLine("V. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);
Console.WriteLine("VI. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);
Console.WriteLine("reset values");
myPBRclas1.k = 100;
myPBRclas1.m = 200;
Console.WriteLine("1. k,m's are: {0}, {1}", myPBRclas1.k,
myPBRclas1.m);
placeHolderPassByRef01cl.func4Swap(myPBRclas1);
Console.WriteLine("2. i,j's and k,m's are: {0}, {1}",myPBRclas1.k,
myPBRclas1.m);
Console.WriteLine("now use 'traditional' swap");
placeHolderPassByRef01cl.func4TraditionalSwap(ref myPBRclas1);
Console.WriteLine("3. i,j's and k,m's are: {0}, {1}",
myPBRclas1.k, myPBRclas1.m);
Console.WriteLine("now use traditional swap from inside of class
(no helper class used)");
myPBRclas1.swap1(ref myPBRclas1.k, ref myPBRclas1.m);
Console.WriteLine("4. i,j's and k,m's are: {0}, {1}",
myPBRclas1.k, myPBRclas1.m);
}

}
}


/////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace console1
{
class PassByRef01
{
public int k, m;
int _i, _j;
public PassByRef01()
{
_i = 10;
_j = 11;
k = 1;
m = 2;
}
public void swap1(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}

public void func1(ref int a, ref int b)
{
a = 1111;
b = 2222;
Console.WriteLine("inside func1, k,m's are: {0}, {1}", k,
m);
}


public void func2(int a, int b)
{
a = 1110000; //since ref not used, this assignment never
made for k,m
b = 2220000;
Console.WriteLine("inside func2, k,m's are: {0}, {1}",
k,m);
}

public void func3(PassByRef01 x)
{
x.k = 66;
x.m = 67;
Console.WriteLine("inside func3, k,m's are: {0}, {1}", k,
m);
}

public void func4TraditionalSwap(ref PassByRef01 y)
{
int temp;
temp = y.k;
y.k = y.m;
y.m = temp;
}



public void func4Swap(PassByRef01 x)
{
int temp1, temp2;
temp1 = x.k;
temp2 = x.m;
x.m = temp1;
x.k = temp2;

Console.WriteLine("inside func3, k,m's are: {0}, {1} and
x.k, x.m are: {2}, {3}", k, m, x.k, x.m);
}


public int i //cannot pass by ref a property
{
get { return _i; }
set { _i = value; }
}
public int j
{
get { return _j; }
set { _j = value; }
}


}

}
/////////////////////// OUTPUT

I. i,j's and k,m's are: 10, 11, 1, 2
inside func2, k,m's are: 1, 2
II. i,j's and k,m's are: 10, 11, 1, 2
inside func1, k,m's are: 1111, 2222
III. i,j's and k,m's are: 10, 11, 1111, 2222
inside func3, k,m's are: 1, 2
IV. i,j's and k,m's are: 10, 11, 66, 67
inside func2, k,m's are: 1, 2
V. i,j's and k,m's are: 10, 11, 66, 67
VI. i,j's and k,m's are: 10, 11, 66, 67
reset values
1. k,m's are: 100, 200
inside func3, k,m's are: 1, 2 and x.k, x.m are: 200, 100
2. i,j's and k,m's are: 200, 100
now use 'traditional' swap
3. i,j's and k,m's are: 100, 200
now use traditional swap from inside of class (no helper class used)
4. i,j's and k,m's are: 200, 100
Press any key to continue . . .
 
M

Marc Gravell

You've done this to death already; and you're still quite, quite
wrong. Ref applies only to the arguments - i.e. "x".

Marc
 
R

raylopez99

You've done this to death already; and you're still quite, quite
wrong. Ref applies only to the arguments - i.e. "x".

Marc

Semantics my friend. You are clearly wrong.

What part of this code don't you understand?


public void func4Swap(PassByRef01 x)
{
int temp1, temp2;
temp1 = x.k;
temp2 = x.m;
x.m = temp1;
x.k = temp2;
Console.WriteLine("inside func3, k,m's are: {0}, {1} and
x.k, x.m are: {2}, {3}", k, m, x.k, x.m);
}


It works to change x.m to x.k. That was the exercise.

All the words in the world and all the spin won't change this
verisimilitude.

RL
 
P

Peter Morris

You are not passing using the "ref" qualifier but you are passing a
reference which may be changed.

To prove you are correct the only way would be to write code which passes
this test:

[TestMethod]
public void ProvePassByReferenceIsEsstentialIsALie()
{
int value1 = 1;
int value2 = 2;

Swap(value1, value2);

Assert.AreEqual(2, value1);
Assert.AreEqual(1, value2);
}

You are not allowed to change the test source code at all. You will not be
able to do it, I am certain.

PS: Saying it is a "lie" is ridiculous. At worst it would be a mistake, not
a lie!



Pete
====
http://mrpmorris.blogspot.com
http://www.capableobjects.com
 
M

Marc Gravell

It works to change x.m to x.k.  That was the exercise.

Only to you. In the context of the book, the exercise was undoubtedly
to swap the values of the *arguments*, not to swap two fields on a
single (class) argument.

Anyway, that is me done on this thread...

Marc
 
C

Ciaran O''Donnell

Your example is not swapping parameters. If i had an array and wanted to swap
two elements in it. I could not just call your function. I would have to
create you class, copy the references from the array to the class's
variables, call the function and then copy the references back. That is
entirely useless, You might as well do the swap inline as it would be less
code and more efficient.
To do a pure swap function which switches the value of the reference
variables passed as parameters you need to use the ref keyword. Why are you
always trying to find a different way to do this?
 
R

raylopez99

Your example is not swapping parameters. If i had an array and wanted to swap
two elements in it. I could not just call your function. I would have to
create you class, copy the references from the array to the class's
variables, call the function and then copy the references back. That is
entirely useless, You might as well do the swap inline as it would be less
code and more efficient.

Thanks for the reply. BTW how do you do anything 'inline' in C#? In C
++ I had the convention down pat but if you can post a short example
it would be helpful for me.
To do a pure swap function which switches the value of the reference
variables passed as parameters you need to use the ref keyword. Why are you
always trying to find a different way to do this?

I'm just showing how using encapsulation you can get around a lot of
bugaboos in C# (this also holds for private variables, because in a
way 'properties' is such an encapsulation).

Now the best answer for the 'traditionalists' was given by Marc and
Peter Morris--you simply redefine your problem so your 'definition'
holds. But, in a way, I stood that principle on its head and
redefined it to suit my needs.

After all, if you are a master coder (and I'm not yet, with about 2
solid months of C# experience) you can find exceptions to every rule.

RL
 
J

Jon Skeet [C# MVP]

Now the best answer for the 'traditionalists' was given by Marc and
Peter Morris--you simply redefine your problem so your 'definition'
holds.  But, in a way, I stood that principle on its head and
redefined it to suit my needs.

Yes - you redefined the problem. What you've done may indeed be
useful, but in no way proves the book wrong.

Jon
 
P

Peter Morris

Now the best answer for the 'traditionalists' was given by Marc and
Peter Morris--you simply redefine your problem so your 'definition'
holds.
<<

I didn't redefine the problem.
01: I haven't seen the original text in the book.
02: I used my brain and guessed what the book is saying, and that is you
cannot change "parameter values" from the caller's point of view unless you
use "ref".

After all, if you are a master coder (and I'm not yet, with about 2
solid months of C# experience) you can find exceptions to every rule.
<<

Or, put another way, what I lack in skill I more than make up for in denial
;-)
 
K

KH

That's a lot of work to 'swap' two ints; try ...

int a = 3;
int b = 5;

a ^= b;
b ^= a;
a ^= b;

// a = 5, b = 3


raylopez99 said:
In the otherwise excellent book C# 3.0 in a Nutshell by Albahari et
al. (3rd edition) (highly recommended--it's packed with information,
and is a desktop reference book) the following statement is made: (p.
39: "The ref modifier is essential in implementing a swap method")

This is untrue. The following program demonstrates it. See how
method "func4Swap" does a swap of two int variables without using ref,
but using a 'helper' class and using only pass by value

However, ref is important when using 'new' in the method calling the
variables to be used (not shown here), or, if you want to permanently
change the variables passed from inside the class doing the passing
(see func1 method and swap1 method below). In fact, I'm not sure you
can do a swap without using ref and without using a 'helper' class as
in the below.

RL

// OUTPUT AT END

// Pass by reference, Pass by Value demonstrated // October 15, 2008

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;


namespace console1
{
class Program
{
static void Main(string[] args)
{
////////////////////////////////////////////
// show pass by ref

PassByRef01 myPBRclas1 = new PassByRef01();

Console.WriteLine("I. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k,myPBRclas1.m);
myPBRclas1.func2(myPBRclas1.i, myPBRclas1.j);
Console.WriteLine("II. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);
myPBRclas1.func1(ref myPBRclas1.k, ref myPBRclas1.m);
Console.WriteLine("III. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);

PassByRef01 placeHolderPassByRef01cl = new PassByRef01();

placeHolderPassByRef01cl.func3(myPBRclas1);
Console.WriteLine("IV. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);

placeHolderPassByRef01cl.func2(myPBRclas1.k, myPBRclas1.m);
Console.WriteLine("V. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);
Console.WriteLine("VI. i,j's and k,m's are: {0}, {1}, {2}, {3}",
myPBRclas1.i, myPBRclas1.j, myPBRclas1.k, myPBRclas1.m);
Console.WriteLine("reset values");
myPBRclas1.k = 100;
myPBRclas1.m = 200;
Console.WriteLine("1. k,m's are: {0}, {1}", myPBRclas1.k,
myPBRclas1.m);
placeHolderPassByRef01cl.func4Swap(myPBRclas1);
Console.WriteLine("2. i,j's and k,m's are: {0}, {1}",myPBRclas1.k,
myPBRclas1.m);
Console.WriteLine("now use 'traditional' swap");
placeHolderPassByRef01cl.func4TraditionalSwap(ref myPBRclas1);
Console.WriteLine("3. i,j's and k,m's are: {0}, {1}",
myPBRclas1.k, myPBRclas1.m);
Console.WriteLine("now use traditional swap from inside of class
(no helper class used)");
myPBRclas1.swap1(ref myPBRclas1.k, ref myPBRclas1.m);
Console.WriteLine("4. i,j's and k,m's are: {0}, {1}",
myPBRclas1.k, myPBRclas1.m);
}

}
}


/////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace console1
{
class PassByRef01
{
public int k, m;
int _i, _j;
public PassByRef01()
{
_i = 10;
_j = 11;
k = 1;
m = 2;
}
public void swap1(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}

public void func1(ref int a, ref int b)
{
a = 1111;
b = 2222;
Console.WriteLine("inside func1, k,m's are: {0}, {1}", k,
m);
}


public void func2(int a, int b)
{
a = 1110000; //since ref not used, this assignment never
made for k,m
b = 2220000;
Console.WriteLine("inside func2, k,m's are: {0}, {1}",
k,m);
}

public void func3(PassByRef01 x)
{
x.k = 66;
x.m = 67;
Console.WriteLine("inside func3, k,m's are: {0}, {1}", k,
m);
}

public void func4TraditionalSwap(ref PassByRef01 y)
{
int temp;
temp = y.k;
y.k = y.m;
y.m = temp;
}



public void func4Swap(PassByRef01 x)
{
int temp1, temp2;
temp1 = x.k;
temp2 = x.m;
x.m = temp1;
x.k = temp2;

Console.WriteLine("inside func3, k,m's are: {0}, {1} and
x.k, x.m are: {2}, {3}", k, m, x.k, x.m);
}


public int i //cannot pass by ref a property
{
get { return _i; }
set { _i = value; }
}
public int j
{
get { return _j; }
set { _j = value; }
}


}

}
/////////////////////// OUTPUT

I. i,j's and k,m's are: 10, 11, 1, 2
inside func2, k,m's are: 1, 2
II. i,j's and k,m's are: 10, 11, 1, 2
inside func1, k,m's are: 1111, 2222
III. i,j's and k,m's are: 10, 11, 1111, 2222
inside func3, k,m's are: 1, 2
IV. i,j's and k,m's are: 10, 11, 66, 67
inside func2, k,m's are: 1, 2
V. i,j's and k,m's are: 10, 11, 66, 67
VI. i,j's and k,m's are: 10, 11, 66, 67
reset values
1. k,m's are: 100, 200
inside func3, k,m's are: 1, 2 and x.k, x.m are: 200, 100
2. i,j's and k,m's are: 200, 100
now use 'traditional' swap
3. i,j's and k,m's are: 100, 200
now use traditional swap from inside of class (no helper class used)
4. i,j's and k,m's are: 200, 100
Press any key to continue . . .
 
R

raylopez99

That's a lot of work to 'swap' two ints; try ...

int a = 3;
int b = 5;

a ^= b;
b ^= a;
a ^= b;

// a = 5, b = 3

Won't work, your example is unsafe code (pointers) not really
supported in C#, and you forgot a temporary variable to store a or b
too boot.

RL
 
R

raylopez99

Yes - you redefined the problem. What you've done may indeed be
useful, but in no way proves the book wrong.

Jon

OK, that's fine. I will make a note of this; perhaps they are right
and I am wrong about the lingo. BTW if you know of an easy way to
make sure only one child window in Window Forms is open when you click
on a menu or button in the parent,please let me know (in words).
Offhand, I can think of several ways to do this, using static magic
numbers to keep track of the child windows open and/or using a
singleton class, etc, but if there's an easier way please do tell.

RL
 
R

raylopez99

Now the best answer for the 'traditionalists' was given by Marc and
Peter Morris--you simply redefine your problem so your 'definition'
holds.
<<

I didn't redefine the problem.
01: I haven't seen the original text in the book.
02: I used my brain and guessed what the book is saying, and that is you
cannot change "parameter values" from the caller's point of view unless you
use "ref".

Yes, that makes you a 'traditionalist'. Perhaps you're right. Anyway
thanks for the input, it was a lerning experience.

RL
 
K

KH

Um no those aren't pointers, and no temp variable is needed. Also C# supports
pointers just fine. Try running this code before running your mouth:

using System;
class App
{
static void Main()
{
Random rand = new Random();
int a = rand.Next();
int b = rand.Next();
Console.WriteLine("a = {0}, b = {1}", a, b);
a ^= b;
b ^= a;
a ^= b;
Console.WriteLine("b = {1}, a = {0}", a, b);
Console.ReadLine();
}
};
 
J

Jon Skeet [C# MVP]

raylopez99 said:
Won't work, your example is unsafe code (pointers) not really
supported in C#, and you forgot a temporary variable to store a or b
too boot.

You didn't try it, did you? The whole point of the example is to show
that no temporary variable is needed (for integers), and certainly
there are no pointers involved. Hint: ^ is the XOR operator in C#.
 
R

raylopez99

You didn't try it, did you? The whole point of the example is to show
that no temporary variable is needed (for integers), and certainly
there are no pointers involved. Hint: ^ is the XOR operator in C#.

Yeah, neat trick, I just tried it. It only works for binary systems
of base 2.

Lern something new everyday, Vern.

RL
 
F

Family Tree Mike

That code does not compile, as Booleans (boolean is not defined), can not be
multiplied or added.

Operator '*' cannot be applied to operands of type 'bool' and 'bool'
C:\...\Program.cs 24 9 SwapCode
Operator '+' cannot be applied to operands of type 'bool' and 'bool'
C:\...\Program.cs 24 9 SwapCode


Who is this guy? Dude read the first
sentence:http://en.wikipedia.org/wiki/Binary_numeral_system

Binary and base-2 numbering are synonomous.

Isn't that what I said?

Now solve this:

boolean x;
if ((x*!x == (!x + x))) {Console.WriteLine ("KH is a dunce");} else
{Console.Write("RayLopez is a fool");} //evaluates to true

//DeMorgan's Theorem of Boolean (binary) base-2 algebra.

RL
 
R

raylopez99

Well then I am the greater fool it seems.

Perhaps a BitArray is called for, then defining a class that can have
members who can be added and subtracted and multiplied according to
boolean algebra, which perhaps might require a class defining the laws
of boolean algebra. Harder than I thought.

Also my initial formula for deMorgan's law is off a bit I believe.

RL
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top