Pass-by-reference and ref [duplicate] - c#

This question already has answers here:
What is the difference between 2 methods with ref object par and without?
(5 answers)
Closed 8 years ago.
I have been reading a bit the tutorials on MSDN to get my head around pass-by-reference in C#, ref and out and I came across the following code sample:
using System;
class TheClass
{
public int x;
}
struct TheStruct
{
public int x;
}
class TestClass
{
public static void structtaker(TheStruct s)
{
s.x = 5;
}
public static void classtaker(TheClass c)
{
c.x = 5;
}
public static void Main()
{
TheStruct a = new TheStruct();
TheClass b = new TheClass();
a.x = 1;
b.x = 1;
structtaker(a);
classtaker(b);
Console.WriteLine("a.x = {0}", a.x); //prints 1
Console.WriteLine("b.x = {0}", b.x); //prints 5
}
}
The note to this from the tutorial:
This example shows that when a struct is passed to a method, a copy of
the struct is passed, but when a class instance is passed, a reference
is passed.
I totally understood it, but my question is, if a reference is passed in C# to the parameter, why would they need ref as in the following sample:
void tearDown(ref myClass a)
{
a = null;
}
MyClass b = new MyClass();
this.tearDown(ref b);
assert(b == null);
//b is null
??? I thought C# was the same in C - pass-by-value.

In C#, basically all classes as pointers. However, passing by ref/out or not is like passing the pointer to a pointer or the pointer itself.
When you pass a class (as per the first sample) any changes to the classes members are carried over. However, changing the reference to the object itself would not yield the results. Say you replace
public static void classtaker(TheClass c)
{
c.x = 5;
}
With
public static void classtaker(TheClass c)
{
c = new TheClass();
c.x = 5;
}
Since c is not an out or ref paramter, you're reassigning the local pointer to c, not the value of c itself. Since you only modified the .x of the local c, the result would be that b.x == 1 after calling this modified ClassTaker.
Now, as per your second example, since a is a ref parameter, changes to the value a itself will be seen in the calling scope, as in the example, but removing the ref from the call would cause the null assertion to fail.
Basically, ref passing passes what can be thought of as a pointer to your pointer, while calling without ref/out passes a copied pointer to your object data.
EDIT:
The reason one can assign c.X in method scope is because the object c points to the object X, and you'll always get the same pointer to X regardless of the ref/out parameter or not. Instead, ref/out modifies your ability to change the value c as seen by the calling scope.

Related

Difference in code? c# passing an argument by reference

Just trying to see if someone can help me understand this:
What is the difference in usage between these two examples below? When is the second, passing an argument by reference better? Both output the same result.
public static void Sqr(int x)
{
x = x * x;
Console.WriteLine(x);
}
static void Main(string[] args)
{
Sqr(3);
}
2)
public static void Sqr(ref int x)
{
x = x * x;
}
static void Main(string[] args)
{
int a = 3;
Sqr(ref a);
Console.WriteLine(a);
}
Anyone know? I’m clueless here. My problem is that don’t really even know why things are written the way they are!! Any insights?
Both output the same result.
That's because as well as changing whether or not it's a ref parameter, you've changed where you've got the output.
Here's a version of your first piece of code that is closer to the second, so they only differ in whether or not it's a ref parameter:
public static void Sqr(int x)
{
x = x * x;
}
static void Main(string[] args)
{
int a = 3;
Sqr(a);
Console.WriteLine(a);
}
Now the output will be 3, because the value of a hasn't changed - its value was passed to the Sqr method. In your second example, the variable itself is passed by reference, so x in the Sqr method and a in the Main method refer to the same storage location... the change to the value of x can be "seen" via a after the method has returned.
When is the second, passing an argument by reference better?
When you want changes in the parameter to be reflected in the argument. That's relatively rare, and I'd encourage you to use value parameters by default. For example, it would be more idiomatic to write Sqr as:
public static int Sqr(int x)
{
return x * x;
}
Then call it as:
a = Sqr(a);
ref parameters are relatively rarely useful in my experience - but it's important to understand how they work. (You may find my article on parameter passing useful for more details.)

Persistent memory confusion

I'm trying to understand persistent memory in C# and don't know why this code is not keeping the change made in one of the functions.
using System;
using System.Runtime.InteropServices;
public class Test2
{
public struct value
{
public int sz;
}
public static void Main(string[] args)
{
one foo1 = new one(one_full);
two foo2 = new two(two_full);
make_calls(foo1, foo2);
}
public delegate void one(IntPtr ctx);
public static void one_full(IntPtr ctx)
{
/* set sz and see if persists */
GCHandle gch = GCHandle.FromIntPtr(ctx);
value val = (value)gch.Target;
val.sz = 6;
Console.WriteLine("Changed sz to be 6");
}
public delegate void two(IntPtr ctx);
public static void two_full(IntPtr ctx)
{
GCHandle gch = GCHandle.FromIntPtr(ctx);
value val = (value)gch.Target;
Console.Write("sz is = ");
Console.WriteLine(val.sz);
}
public static void make_calls(one f_one, two f_two)
{
value test = new value();
test.sz = 0;
IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(test));
f_one(ptr);
f_two(ptr);
}
}
I know it's missing the free at the end but that is just leading to messy memory management.... I'm looking for if someone can help me out and explain why sz does not stay as the value 6 for when the second function is called...
The output when ran is:
Changed sz to be 6
sz is = 0
It's all because value is a struct.
GCHandle.Alloc takes an object parameter, so if you pass a struct, it has to be boxed.
Later when you use
value val = (value)gch.Target;
it has to be unboxed, so a copy of it is stored in val. Any modifications you make later are made on the copy, not on the boxed struct.
It has nothing to do with GCHandle, it's how value types (structs) work in C#. That's why it's recommended to make value types immutable.
Becuase value is a sturct and structs are not reference types. when you have an instance of a struct and var b = instanceOfStruct then b is a new struct not a reference to instanceOfStruct. Chaning values in b does not reflect to instanceOfStruct.
In your code:
value val = (value)gch.Target;
will create a new instance of value struct which has same values as the structs that gch.Target point to it. Changing val does not change the struct behind gch.Target. The problem is because of a confusion between value types and reference types in C#. If you change value type to a class instead of struct then you will get the desired result. You can also use dynamic to modify the struct which is target of the handle:
dynamic val = gch.Target;
val.sz = 6;

Accessing and changing structs as property vs as field

Ok, I'll start my question saying that I understand the evil behind mutable structs, but I'm working with SFML.net and using a lot of Vector2f and such structs.
What I don't get it is why I can have, and change the values of, a field in a class and can't do the same with a property, in the very same class.
Take a look at this code:
using System;
namespace Test
{
public struct TestStruct
{
public string Value;
}
class Program
{
TestStruct structA;
TestStruct structB { get; set; }
static void Main(string[] args)
{
Program program = new Program();
// This Works
program.structA.Value = "Test A";
// This fails with the following error:
// Cannot modify the return value of 'Test.Program.structB'
// because it is not a variable
//program.structB.Value = "Test B";
TestStruct copy = program.structB;
copy.Value = "Test B";
Console.WriteLine(program.structA.Value); // "Test A"
Console.WriteLine(program.structB.Value); // Empty, as expected
}
}
}
note: I'll build my own classes to cover the same functionality and keep with my mutability, but I can't see a technical reason why I can do one and can't do other.
When you access a field, you are accessing the actual struct. When you access it through property, you call a method that returns whatever is stored in the property. In the case of a struct, which is a value type, you will get back a copy of the struct. Apparently that copy is not a variable and cannot be changed.
Section "1.7 Structs" of the C# language specification 5.0 says:
With classes, it is possible for two variables to reference the same
object and thus possible for operations on one variable to affect the
object referenced by the other variable. With structs, the variables
each have their own copy of the data, and it is not possible for
operations on one to affect the other.
That explains that you will receive a copy of the struct and not be able to modify the original struct. However, it doesn't describe why it isn't allowed.
Section "11.3.3" of the specifcation:
When a property or indexer of a struct is the target of an assignment,
the instance expression associated with the property or indexer access
must be classified as a variable. If the instance expression is
classified as a value, a compile-time error occurs. This is described
in further detail in §7.17.1.
So the returned "thing" from the get accessor is a value and not a variable. That explains the wording in the error message.
The specification also contains an example in section 7.17.1 that is nearly identical to your code:
Given the declarations:
struct Point
{
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int X {
get { return x; }
set { x = value; }
}
public int Y {
get { return y; }
set { y = value; }
}
}
struct Rectangle
{
Point a, b;
public Rectangle(Point a, Point b) {
this.a = a;
this.b = b;
}
public Point A {
get { return a; }
set { a = value; }
}
public Point B {
get { return b; }
set { b = value; }
}
}
in the example
Point p = new Point();
p.X = 100;
p.Y = 100;
Rectangle r = new Rectangle();
r.A = new Point(10, 10);
r.B = p;
the assignments to p.X, p.Y, r.A, and r.B are permitted because p and r are variables. However, in the example
Rectangle r = new Rectangle();
r.A.X = 10;
r.A.Y = 10;
r.B.X = 100;
r.B.Y = 100;
the assignments are all invalid, since r.A and r.B are not variables.
Although properties look like variables, each property is really a combination of a get method and/or a set method. Typically a property get method will return a copy of what's in some variable or array slot, and a put method will copy its parameter into that variable or array slot. If one wants to do something like someVariable = someObject.someProeprty; or someobject.someProperty = someVariable;, it won't matter that those statements end up being executed as var temp=someObject.somePropertyBackingField; someVariable=temp; and var temp=someVariable; someObject.somePropertyBackingField=temp;, respectively. On the other hand, there are some operations which can be done with fields but cannot be done with properties.
If an object George exposes a field named Field1, then code may pass George.Field as a ref or out parameter to another method. Additionally, if the type of Field1 is a value type with exposed fields, then an attempt to access those fields will access the fields of the struct that is stored within George. If Field1 has exposed properties or methods, then accessing those will cause George.Field1 to be passed to those methods as though it were a ref parameter.
If George exposes a property named Property1, then an access of Property1 which is not the left side of an assignment operator will call the "get" method and store its result in a temporary variable. An attempt to read a field of Property1 will read that field from the temporary variable. An attempt to call a property getter or method on Property1 will pass that temporary variable as a ref parameter to that method and then discard it after the method returns. Within the method or property getter or method, this will refer to the temporary variable, and any changes the method makes to this will be discarded.
Because it would make no sense to write to fields of a temporary variable, attempts to write to fields of a property are forbidden. Additionally, present versions of the C# compiler will guess that property setters would be likely to modify this and will thus forbid any use of property setters even when they would in fact not modify the underlying structure [e.g. the reason ArraySegment includes an indexed get method and not an indexed set method is that if one were to try to say e.g. thing.theArraySegment[3] = 4; the compiler would think one was trying to trying to modify the structure returned by the theArraySegment property, rather than modify the array whose reference is encapsulated therein]. It would be extremely useful if one could specify that particular structure methods will modify this and should not be invokable on structure properties, but as yet no mechanism exists for that.
If one wants to write to a field contained within a property, the best pattern is usually:
var temp = myThing.myProperty; // Assume `temp` is a coordinate-point structure
temp.X += 5;
myThing.myProperty = temp;
If the type of myProperty is designed to encapsulate a fixed set of related but independent values (such as the coordinates of a point), it's best if it exposes those variables as fields. Although some people seem to prefer to design structs so as to require constructs like:
var temp = myThing.myProperty; // Assume `temp` is some kind of XNA Point structure
myThing.myProperty = new CoordinatePoint(temp.X+5, temp.Y);
I would regard such code as less readable, less efficient, and more error-prone than the previous style. Among other things, if CoordinatePoint happens to e.g. expose a constructor with parameters X,Y,Z as well as a constructor which takes parameters X,Y and assumes Z is zero, code like the second form would zero out Z without any indication that it was doing so (intentionally or unintentionally). By contrast, if X is an exposed field, it's much clearer that the first form would only modify X.
In some cases, it may be helpful for a class to expose an internal field or array slot via a method that passes it as a ref parameter to a user-defined routine, e.g. a List<T>-like class might expose:
delegate void ActByRef<T1>(ref T1 p1);
delegate void ActByRef<T1,T2>(ref T1 p1, ref T2 p2);
void ActOnItem(int index, ActByRef<T> proc)
{
proc(ref BackingArray[index]);
}
void ActOnItem<PT>(int index, ActByRef<T,PT> proc, ref PT extraParam)
{
proc(ref BackingArray[index], ref extraParam);
}
Code which had a FancyList<CoordinatePoint> and wanted to add some local variable dx to field X of item 5 in iit could then do:
myList.ActOnItem(5, (ref Point pt, ref int ddx) => pt.X += ddx, ref dx);
Note that this approach would allow in-place modification of data in the list, and even allow the use of such methods as Interlocked.CompareExchange). Unfortunately, there's no possible mechanism by which a type which derives from List<T> can support such a method, and no mechanism by which a type which does support such a method can be passed to code which expects a List<T>.

Generic Swap difficulty

I'm coming from C++ where it's easy to do something like this:
template<class T>
void Swap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
and then use it to swap values in a container:
std::vector<int> someInts;
someInts.push_back(1);
someInts.push_back(2);
Swap(someInts[0], someInts[1]);
However, upon attempting to do the same thing in C#
void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
I get the error "property or indexer may not be passed as an out or ref parameter"
Why is this and how can I overcome it?
Many thanks
You cannot use indexers or properties ref parameters. The reason is you are retuning a reference to the object but not the location so any effect the function would have would not actually change the source as it wouldn't write it back to the location (i.e. not call the setter in the case of a property). You need to pass the array into the method so that the method can set values an indexes as well as know what values to swap.
Properties and the indexer are actually methods (created by the compiler behind the scenes), so I suppose it is not possible to do call-by-reference on them. However you could write a method like this:
public void Swap<T>(T[] data, int a, int b) {
T temp = data[a];
data[a] = data[b];
data[b] = temp;
}
Swap(someInts, 0, 1);

Use of 'ref' keyword in C# [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicates:
Why use ref keyword when passing an Object?
When to pass ref keyword in
What is the correct usage of the 'ref' keyword in C#. I believe there has been plenty of discussion threads on this, but what is not clear to me is:
Is the ref keyword required if you are passing in a reference object? I mean when you create an object in the heap, is it not always passed by reference. Does this have to be explicitly marked as a ref?
Using ref means that the reference is passed to the function.
The default behaviour is that the function receives a new reference to the same object. This means if you change the value of the reference (e.g. set it to a new object) then you are no longer pointing to the original, source object. When you pass using ref then changing the value of the reference changes the source reference - because they are the same thing.
Consider this:
public class Thing
{
public string Property {get;set;}
}
public static void Go(Thing thing)
{
thing = new Thing();
thing.Property = "Changed";
}
public static void Go(ref Thing thing)
{
thing = new Thing();
thing.Property = "Changed";
}
Then if you run
var g = new Thing();
// this will not alter g
Go(g);
// this *will* alter g
Go(ref g);
There is a lot of confusing misinformation in the answers here. The easiest way to understand this is to abandon the idea that "ref" means "by reference". A better way to think about it is that "ref" means "I want this formal parameter on the callee side to be an alias for a particular variable on the caller side".
When you say
void M(ref int y) { y = 123; }
...
int x = 456;
M(ref x);
that is saying "during the call to M, the formal parameter y on the callee side is another name for the variable x on the caller side". Assigning 123 to y is exactly the same as assigning 123 to x because they are the same variable, a variable with two names.
That's all. Don't think about reference types or value types or whatever, don't think about passing by reference or passing by value. All "ref" means is "temporarily make a second name for this variable".
I believe the ref keyword indicates that you are passing the object by reference, not by value. For example:
void myfunction(ref object a) {
a = new Something();
}
would change the value of a in the calling function
However,
void myfunction(object a) {
a = new Something();
}
would change the value of a locally, but not in the calling function. You can still change PROPERTIES of the item, but you cannot set the value of the item itself. For example;
a.someproperty = value;
would work in both cases.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace InOutRef
{
static class InOutRef
{
public static void In(int i)
{
Console.WriteLine(i);
i=100;
Console.WriteLine(i);
}
public static void Ref(ref int i)
{
Console.WriteLine(i);
i=200;
Console.WriteLine(i);
}
public static void Out(out int i)
{
//Console.WriteLine(i); //Error Unsigned Ref
i=300;
Console.WriteLine(i);
}
}
class Program
{
static void Main(string[] args)
{
int i = 1;
InOutRef.In(i); //passed by value (in only)
Debug.Assert(i==1);
InOutRef.Ref(ref i); //passed by ref (in or out)
Debug.Assert(i == 200);
InOutRef.Out(out i); //passed by as out ref (out only)
Debug.Assert(i == 300);
}
}
}
I can't be any more literal on my answer. The code will not remember reference chanages such as the classic Java swap question when using in. However, when using ref, it will be similar to VB.NET as it will remember the changes in and out. If you use the out parameter it means that it must be declared before you return (this is enforced by the compiler).
Output:
1 //1 from main
100 //100 from in
1 //1 is NOT remembered from In
200 //200 from ref
//should be 200 here but out enforces out param (not printed because commented out)
300 //300 is out only
Press any key to continue . . .

Categories