Swap with out vs ref - c#

What's the better way of implementing a swap function? How would using ref be better than using out, or vice versa? Any other benefits or drawbacks that will occur from using one or the other?
swapref(ref T a, ref T b) {
T temp = a;
a = b;
b = temp;
}
swapout(out T a, out T b)
{
T temp = a;
a = b;
b = temp;
}

By definition a Swap() function requires that the parameters passed have values. Therefore you should use ref.
Using out would imply that (and allow) uninitialized parameters could be passed (and in fact should not compile since you would be using an unassigned parameter)

If I modify your swapout method to something closer to compiling...
static void swapout<T>(out T a, out T b)
{
T temp = a; // ERROR: Use of unassigned out parameter 'a'
a = b; // ERROR: Use of unassigned out parameter 'b'
b = temp;
}
...it still produces the errors you see in the comments.
When a parameter is modified with out it is treated as uninitialized and it is the responsibility of the method to assign it a definite value. The whole point of a swap method is to operate on values passed to it, but with out the values of those parameters are off limits because the caller is not required to pass an initialized variable. Therefore, to answer your question, not only is using ref the superior solution, but using out is not a workable solution at all.

Definite assignment is a big deal in C#, it keeps you out of trouble by ensuring that your code has assigned a value to a variable before it is used. The core reason that the language distinguishes between ref and out. Other languages don't draw this distinction, they just have syntax for "pass by value" vs "pass by reference". In VB.NET that's ByVal vs ByRef for example. By using out instead of ref you clearly state the data flow and promise that the variable doesn't have to be assigned before the call and will be assigned afterwards. The compiler verifies this and complains if you break that rule.
What you can't do is swap a "not assigned" state. No syntax exists that lets you express that a variable is not assigned after an operation. It therefore follows that you must use ref. Nullable gives you options, not in scope here.

Related

Problem when using arrays as parameters c# [duplicate]

Consider the following code (I have purposefully written MyPoint to be a reference type for this example)
public class MyPoint
{
public int x;
public int y;
}
It is universally acknowledged (in C# at least) that when you pass by reference, the method contains a reference to the object being manipulated, whereas when you pass by value, the method copies the value being manipulated, thus the value in global scope is not affected.
Example:
void Replace<T>(T a, T b)
{
a = b;
}
int a = 1;
int b = 2;
Replace<int>(a, b);
// a and b remain unaffected in global scope since a and b are value types.
Here is my problem; MyPoint is a reference type, thus I would expect the same operation on Point to replace a with b in global scope.
Example:
MyPoint a = new MyPoint { x = 1, y = 2 };
MyPoint b = new MyPoint { x = 3, y = 4 };
Replace<MyPoint>(a, b);
// a and b remain unaffected in global scope since a and b...ummm!?
I expected a and b to point to the same reference in memory...can someone please clarify where I have gone wrong?
Re: OP's Assertion
It is universally acknowledged (in C# at least) that when you pass by reference, the method contains a reference to the object being manipulated, whereas when you pass by value, the method copies the value being manipulated ...
TL;DR
There's more to it than that. Unless you pass variables with the ref or out keywords, C# passes variables to methods by value, irrespective of whether the variable is a value type or a reference type.
If passed by reference, then the called function may change the variable's address at the call-site (i.e. change the original calling function's variable's assignment).
If a variable is passed by value:
if the called function re-assigns the variable, this change is local to the called function only, and will not affect the original variable in the calling function
however, if changes are made to the variable's fields or properties by the called function, it will depend on whether the variable is a value type or a reference type in order to determine whether the calling function will observe the changes made to this variable.
Since this is all rather complicated, I would recommend avoiding passing by reference if possible (instead, if you need to return multiple values from a function, use a composite class, struct, or Tuples as a return type instead of using the ref or out keywords on parameters)
Also, when passing reference types around, a lot of bugs can be avoided by not changing (mutating) fields and properties of an object passed into a method (for example, use C#'s immutable properties to prevent changes to properties, and strive to assign properties only once, during construction).
In Detail
The problem is that there are two distinct concepts:
Value Types (e.g. int) vs Reference Types (e.g. string, or custom classes)
Passing by Value (default behaviour) vs Passing by Reference(ref, out)
Unless you explicitly pass (any) variable by reference, by using the out or ref keywords, parameters are passed by value in C#, irrespective of whether the variable is a value type or reference type.
When passing value types (such as int, float or structs like DateTime) by value (i.e. without out or ref), the called function gets a copy of the entire value type (via the stack).
Any change to the value type, and any changes to any properties / fields of the copy will be lost when the called function is exited.
However, when passing reference types (e.g. custom classes like your MyPoint class) by value, it is the reference to the same, shared object instance which is copied and passed on the stack.
This means that:
If the passed object has mutable (settable) fields and properties, any changes to those fields or properties of the shared object are permanent (i.e. any changes to x or y are seen by anyone observing the object)
However, during method calls, the reference itself is still copied (passed by value), so if the parameter variable is reassigned, this change is made only to the local copy of the reference, so the change will not be seen by the caller. This is why your code doesn't work as expected
What happens here:
void Replace<T>(T a, T b) // Both a and b are passed by value
{
a = b; // reassignment is localized to method `Replace`
}
for reference types T, means that the local variable (stack) reference to the object a is reassigned to the local stack reference b. This reassign is local to this function only - as soon as scope leaves this function, the re-assignment is lost.
If you really want to replace the caller's references, you'll need to change the signature like so:
void Replace<T>(ref T a, T b) // a is passed by reference
{
a = b; // a is reassigned, and is also visible to the calling function
}
This changes the call to call by reference - in effect we are passing the address of the caller's variable to the function, which then allows the called method to alter the calling method's variable.
However, nowadays:
Passing by reference is generally regarded as a bad idea - instead, we should either pass return data in the return value, and if there is more than one variable to be returned, then use a Tuple or a custom class or struct which contains all such return variables.
Changing ('mutating') a shared value (and even reference) variable in a called method is frowned upon, especially by the Functional Programming community, as this can lead to tricky bugs, especially when using multiple threads. Instead, give preference to immutable variables, or if mutation is required, then consider changing a (potentially deep) copy of the variable. You might find topics around 'pure functions' and 'const correctness' interesting further reading.
Edit
These two diagrams may help with the explanation.
Pass by value (reference types):
In your first instance (Replace<T>(T a,T b)), a and b are passed by value. For reference types, this means the references are copied onto the stack and passed to the called function.
Your initial code (I've called this main) allocates two MyPoint objects on the managed heap (I've called these point1 and point2), and then assigns two local variable references a and b, to reference the points, respectively (the light blue arrows):
MyPoint a = new MyPoint { x = 1, y = 2 }; // point1
MyPoint b = new MyPoint { x = 3, y = 4 }; // point2
The call to Replace<Point>(a, b) then pushes a copy of the two references onto the stack (the red arrows). Method Replace sees these as the two parameters also named a and b, which still point to point1 and point2, respectively (the orange arrows).
The assignment, a = b; then changes the Replace methods' a local variable such that a now points to the same object as referenced by b (i.e. point2). However, note that this change is only to Replace's local (stack) variables, and this change will only affect subsequent code in Replace (the dark blue line). It does NOT affect the calling function's variable references in any way, NOR does this change the point1 and point2 objects on the heap at all.
Pass by reference:
If however we we change the call to Replace<T>(ref T a, T b) and then change main to pass a by reference, i.e. Replace(ref a, b):
As before, two point objects allocated on the heap.
Now, when Replace(ref a, b) is called, while mains reference b (pointing to point2) is still copied during the call, a is now passed by reference, meaning that the "address" to main's a variable is passed to Replace.
Now when the assignment a = b is made ...
It is the the calling function, main's a variable reference which is now updated to reference point2. The change made by the re-assignment to a is now seen by both main and Replace. There are now no references to point1
Changes to (heap allocated) object instances are seen by all code referencing the object
In both scenarios above, no changes were actually made to the heap objects, point1 and point2, it was only local variable references which were passed and re-assigned.
However, if any changes were actually made to the heap objects point1 and point2, then all variable references to these objects would see these changes.
So, for example:
void main()
{
MyPoint a = new MyPoint { x = 1, y = 2 }; // point1
MyPoint b = new MyPoint { x = 3, y = 4 }; // point2
// Passed by value, but the properties x and y are being changed
DoSomething(a, b);
// a and b have been changed!
Assert.AreEqual(53, a.x);
Assert.AreEqual(21, b.y);
}
public void DoSomething(MyPoint a, MyPoint b)
{
a.x = 53;
b.y = 21;
}
Now, when execution returns to main, all references to point1 and point2, including main's variables a and b, which will now 'see' the changes when they next read the values for x and y of the points. You will also note that the variables a and b were still passed by value to DoSomething.
Changes to value types affect the local copy only
Value types (primitives like System.Int32, System.Double) and structs (like System.DateTime, or your own structs) are allocated on the stack, not the heap, and are copied verbatim onto the stack when passed into a call. This leads to a major difference in behaviour, since changes made by the called function to a value type field or property will only be observed locally by the called function, because it only will be mutating the local copy of the value type.
e.g. Consider the following code with an instance of the mutable struct, System.Drawing.Rectangle
public void SomeFunc(System.Drawing.Rectangle aRectangle)
{
// Only the local SomeFunc copy of aRectangle is changed:
aRectangle.X = 99;
// Passes - the changes last for the scope of the copied variable
Assert.AreEqual(99, aRectangle.X);
} // The copy aRectangle will be lost when the stack is popped.
// Which when called:
var myRectangle = new System.Drawing.Rectangle(10, 10, 20, 20);
// A copy of `myRectangle` is passed on the stack
SomeFunc(myRectangle);
// Test passes - the caller's struct has NOT been modified
Assert.AreEqual(10, myRectangle.X);
The above can be quite confusing and highlights why it is good practice to create your own custom structs as immutable.
The ref keyword works similarly to allow value type variables to be passed by reference, viz that the 'address' of the caller's value type variable is passed onto the stack, and assignment of the caller's assigned variable is now directly possible.
C# is actually pass by value. You get the illusion it's pass by reference, because when you pass a reference type you get a copy of the reference (the reference was passed by value). However, since your replace method is replacing that reference copy with another reference, it's effectively doing nothing (The copied reference goes out of scope immediately). You can actually pass by reference by adding the ref keyword:
void Replace<T>(ref T a, T b)
{
a = b;
}
This will get you your desired result, but in practice is a little strange.
In C# all the params that you pass to a method are passed by value.
Now before you shout keep on reading:
A value-type's value is the data that is copied while a reference type's value is actually a reference.
So when you pass an objects reference to a method and change that object then the changes will reflect outside the method as well since you are manipulating the same memory the object was allocated.
public void Func(Point p){p.x = 4;}
Point p = new Point {x=3,y=4};
Func(p);
// p.x = 4, p.y = 4
Now Lets look at this method:
public void Func2(Point p){
p = new Point{x=5,y=5};
}
Func2(p);
// p.x = 4, p.y = 4
So no changed occurred here and why? Your method simply created a new Point and changed p's reference(Which was passed by value) and therefore the change was local. You didn't manipulate the point, you changed the reference and you did locally.
And there comes the ref keyword that saves the day:
public void Func3(ref Point p){
p = new Point{x=5,y=5};
}
Func3(ref p);
// p.x = 5, p.y = 5
The same occurred in your example. You assigned a point with a new reference, but you did it locally.
C# is passing reference types objects not by reference, but rather it's passing the reference by value. Meaning you can mess around with their insides, but you can't change the assignment itself.
Read this great piece by Jon Skeet for deeper understanding.
Have a look on behavior by a simple program in C#:
class Program
{
static int intData = 0;
static string stringData = string.Empty;
public static void CallByValueForValueType(int data)
{
data = data + 5;
}
public static void CallByValueForRefrenceType(string data)
{
data = data + "Changes";
}
public static void CallByRefrenceForValueType(ref int data)
{
data = data + 5;
}
public static void CallByRefrenceForRefrenceType(ref string data)
{
data = data +"Changes";
}
static void Main(string[] args)
{
intData = 0;
CallByValueForValueType(intData);
Console.WriteLine($"CallByValueForValueType : {intData}");
stringData = string.Empty;
CallByValueForRefrenceType(stringData);
Console.WriteLine($"CallByValueForRefrenceType : {stringData}");
intData = 0;
CallByRefrenceForValueType(ref intData);
Console.WriteLine($"CallByRefrenceForValueType : {intData}");
stringData = string.Empty;
CallByRefrenceForRefrenceType(ref stringData);
Console.WriteLine($"CallByRefrenceForRefrenceType : {stringData}");
Console.ReadLine();
}
}
Output:
You're not understanding what passing by reference means. Your Replace method is creating a copy of the Point object--passing by value (which is actually the better way of doing it).
To pass by reference, so that a and b both reference the same point in memory, you need add "ref" to the signature.
You dont get it right.
It is similar like Java - everything is passed by value! But you do have to know, what the value is.
In primitive data types, the value is the number itself. In other cases it is reference.
BUT, if you copy reference to another variable, it holds same reference, but does not reference the variable (thus it is not pass by reference known in C++).
By default c# passes ALL arguements by value... that is why a and b remain unaffected in global scope in your examples. Here's a reference for those down voters.
To add more detail...in .NET, C# methods, using the default "pass by value" assigned to all parameters, reference types act differently in two scenarios. In the case of all reference types using classes (System.Object types), a copy of the "pointer" (to a memory block) to the original class or object is passed in and assigned to the method's parameter or variable name. This pointer is a value, too, and copied on the stack in memory where all value types are store. The value of the object isn't stored just a copy of its pointer, that points back to the original cl;ass object. I believe this is a 4-byte value. That's what is physically passed and stored in methods for all reference types. So, you now have a new method parameter or variable with a pointer assigned to it still pointing back to the original class object outside the method. You can now do two things to the new variable with the copied pointer value:
You can change the ORIGINAL object outside the method by changing its properties iniside your method. If "MyObject" is your variable with the copied pointer, you would do MyObject.myproperty = 6;, which changed the "myproperty" inside the original object outside the method. You did this as you passed in a pointer to the original object and assigned it to a new variable in your method. Note that this DOES change the referenced object outside the method.
Or, setting your variable with copied pointer to a new object and new pointer like so: MyObject = new SomeObject(); Here, we destroyed the old copied pointer assigned the variable above and assigned it to a new pointer to a new object! Now we have lost connection to the outside object and only changing a new object.

Why ref keyword needs to be initialized and out keyword needn't to be initialized before it is it passed?

Below are the MSDN reference links for the Out keyword and the Ref keyword.
Out Keyword
Ref Keyword
Both Ref and Out keywords are pass by reference, then why it is required for one to be initialized and the other needn't to be initialized? Is it something by design convention or is there any other reason/meaning behind the same? Need some help.
A reference is basically the Address part of:
[Address] => points to => [Object]
The ref keyword passes the address of an existing object. The method can use the object at that address or instantiate an entirely new one (but at the same address). The address does need to be initialized, even if the value it holds is null. (Below is a little test driver that show this).
The out keyword says that the method must instantiate (or set to null) an object (not an address) that it returns.
[TestMethod]
public void TestMethod2()
{
MyClass myClass;
myClass = new MyClass(1);
// Initializing the ADDRESS as an existing object.
ByRefDemo(ref myClass);
Console.WriteLine($"Returned value is: {myClass}");
// Initializing the ADDRESS as a null object.
myClass = null;
ByRefDemo(ref myClass);
Console.WriteLine($"Returned value is: {myClass}");
}
class MyClass
{
public MyClass(int value)
{
Value = value;
}
public int Value { get; }
public override string ToString() => $"{Value}";
}
void ByRefDemo(ref MyClass addressOf)
{
var value = addressOf == null ? "NULL" : $"{addressOf}";
Console.WriteLine($"Incoming value is: {value}" );
addressOf = new MyClass(2);
}
You may be looking at this from the wrong perspective.
ref and out are both modifiers that are used in a function signature declaration, but they aren't really similar otherwise. They shouldn't both do the same thing or have the same requirements.
ref tells the compiler that you want a reference to something that would ordinarily be passed by value. You are passing a live object. Therefore, you have to already have a live object that you want to pass.
out tells the compiler that you want an output variable. It acts precisely as another function return value. Just like the function return value, anything assigned to it before the function is called will be overwritten by the value set in the function. So your output variable may be declared and even initialized or set but its value will be overwritten anyway.
You may use ref to do something similar to out, which may be dangerous and may violate best practices or other standards that you're using (it does commonly). Using out instead of ref won't work though.
There will be occasions when you're programming, that you aren't programming for yourself; you're programming for the benefit of someone else. Imagine you're writing some awesome parsing library or something..
There aren't any surprises for someone calling your method if they're passing you some reference type by value semantics; C# makes sure your method gets a copy of their reference. You can modify the contents of the instance at end of the reference, sure, but you can't surprise the caller by swapping out the reference they gave you for something else
It's different with things passed by original reference; they could give you some carefully crafted object that took hours to make and you could trash it by setting the reference to null. It would be nice for them to know this ahead of time, so they could keep their own copy of the reference
As such, with your arguments that are passed by original reference rather than by copy, you have 3 choices with which to decorate them to help indicate your intentions towards their data:
out - "don't bother spending hours crafting the perfect object; I will overwrite the reference you give"
ref - "give me some data, but don't be surprised if I replace your object with another. Keep your own reference if you're precious about losing it"
in - "the pass will be done by original reference but I promise I won't swap it out for something else"
The compiler helps you make the first and last promise by insisting that you do set an out/don't set an in; and this is essentially the answer to your question: in/out/ref behave the way they do by design to help you make the promises you make when you use one of them on an argument
out and ref perhaps don't seem to have much of a point if you're looking at things from "I'm going to write this method here and use it there" but it does help describe to someone else (who cannot see the inner workings of what your method does) what you will do with their thing they provide, and that's quite important but easy to overlook if you you don't have that "external caller" perspective in mind

Misunderstanding of C# class vs struct

I was trying to implement an automatic differentiation C# lib based on a Python example but I have an issue with the lambda / local function (kind of vague, not sure why it doesn't work).
I even implemented the thing in another language: Kotlin succesfully ... So I probably have a misunderstanding of some programming part either something related to narrow / deep copy or lambda / local function.
public static Value operator +(Value a, Value b)
{
var ret = new Value(a.Data + b.Data, new[]{a, b});
void B()
{
a.Gradient += ret.Gradient;
b.Gradient += ret.Gradient;
}
ret._backward = B;
return ret;
}
I give this little piece of code but it's better to look at the whole: full code with tests in 3 languages
The Gradient doesn't change strangely, I don't know how to give a better explanation of the problem.
Try changing Value from being struct i.e. value type to class i.e. reference type:
public class Value
{
...
}
As docs state:
A variable of a value type contains an instance of the type. This differs from a variable of a reference type, which contains a reference to an instance of the type. By default, on assignment, passing an argument to a method, and returning a method result, variable values are copied. In the case of value-type variables, the corresponding type instances are copied
so changes to fields of local a and b inside operator + function will not affect the values outside.

C# pass by value vs. pass by reference

Consider the following code (I have purposefully written MyPoint to be a reference type for this example)
public class MyPoint
{
public int x;
public int y;
}
It is universally acknowledged (in C# at least) that when you pass by reference, the method contains a reference to the object being manipulated, whereas when you pass by value, the method copies the value being manipulated, thus the value in global scope is not affected.
Example:
void Replace<T>(T a, T b)
{
a = b;
}
int a = 1;
int b = 2;
Replace<int>(a, b);
// a and b remain unaffected in global scope since a and b are value types.
Here is my problem; MyPoint is a reference type, thus I would expect the same operation on Point to replace a with b in global scope.
Example:
MyPoint a = new MyPoint { x = 1, y = 2 };
MyPoint b = new MyPoint { x = 3, y = 4 };
Replace<MyPoint>(a, b);
// a and b remain unaffected in global scope since a and b...ummm!?
I expected a and b to point to the same reference in memory...can someone please clarify where I have gone wrong?
Re: OP's Assertion
It is universally acknowledged (in C# at least) that when you pass by reference, the method contains a reference to the object being manipulated, whereas when you pass by value, the method copies the value being manipulated ...
TL;DR
There's more to it than that. Unless you pass variables with the ref or out keywords, C# passes variables to methods by value, irrespective of whether the variable is a value type or a reference type.
If passed by reference, then the called function may change the variable's address at the call-site (i.e. change the original calling function's variable's assignment).
If a variable is passed by value:
if the called function re-assigns the variable, this change is local to the called function only, and will not affect the original variable in the calling function
however, if changes are made to the variable's fields or properties by the called function, it will depend on whether the variable is a value type or a reference type in order to determine whether the calling function will observe the changes made to this variable.
Since this is all rather complicated, I would recommend avoiding passing by reference if possible (instead, if you need to return multiple values from a function, use a composite class, struct, or Tuples as a return type instead of using the ref or out keywords on parameters)
Also, when passing reference types around, a lot of bugs can be avoided by not changing (mutating) fields and properties of an object passed into a method (for example, use C#'s immutable properties to prevent changes to properties, and strive to assign properties only once, during construction).
In Detail
The problem is that there are two distinct concepts:
Value Types (e.g. int) vs Reference Types (e.g. string, or custom classes)
Passing by Value (default behaviour) vs Passing by Reference(ref, out)
Unless you explicitly pass (any) variable by reference, by using the out or ref keywords, parameters are passed by value in C#, irrespective of whether the variable is a value type or reference type.
When passing value types (such as int, float or structs like DateTime) by value (i.e. without out or ref), the called function gets a copy of the entire value type (via the stack).
Any change to the value type, and any changes to any properties / fields of the copy will be lost when the called function is exited.
However, when passing reference types (e.g. custom classes like your MyPoint class) by value, it is the reference to the same, shared object instance which is copied and passed on the stack.
This means that:
If the passed object has mutable (settable) fields and properties, any changes to those fields or properties of the shared object are permanent (i.e. any changes to x or y are seen by anyone observing the object)
However, during method calls, the reference itself is still copied (passed by value), so if the parameter variable is reassigned, this change is made only to the local copy of the reference, so the change will not be seen by the caller. This is why your code doesn't work as expected
What happens here:
void Replace<T>(T a, T b) // Both a and b are passed by value
{
a = b; // reassignment is localized to method `Replace`
}
for reference types T, means that the local variable (stack) reference to the object a is reassigned to the local stack reference b. This reassign is local to this function only - as soon as scope leaves this function, the re-assignment is lost.
If you really want to replace the caller's references, you'll need to change the signature like so:
void Replace<T>(ref T a, T b) // a is passed by reference
{
a = b; // a is reassigned, and is also visible to the calling function
}
This changes the call to call by reference - in effect we are passing the address of the caller's variable to the function, which then allows the called method to alter the calling method's variable.
However, nowadays:
Passing by reference is generally regarded as a bad idea - instead, we should either pass return data in the return value, and if there is more than one variable to be returned, then use a Tuple or a custom class or struct which contains all such return variables.
Changing ('mutating') a shared value (and even reference) variable in a called method is frowned upon, especially by the Functional Programming community, as this can lead to tricky bugs, especially when using multiple threads. Instead, give preference to immutable variables, or if mutation is required, then consider changing a (potentially deep) copy of the variable. You might find topics around 'pure functions' and 'const correctness' interesting further reading.
Edit
These two diagrams may help with the explanation.
Pass by value (reference types):
In your first instance (Replace<T>(T a,T b)), a and b are passed by value. For reference types, this means the references are copied onto the stack and passed to the called function.
Your initial code (I've called this main) allocates two MyPoint objects on the managed heap (I've called these point1 and point2), and then assigns two local variable references a and b, to reference the points, respectively (the light blue arrows):
MyPoint a = new MyPoint { x = 1, y = 2 }; // point1
MyPoint b = new MyPoint { x = 3, y = 4 }; // point2
The call to Replace<Point>(a, b) then pushes a copy of the two references onto the stack (the red arrows). Method Replace sees these as the two parameters also named a and b, which still point to point1 and point2, respectively (the orange arrows).
The assignment, a = b; then changes the Replace methods' a local variable such that a now points to the same object as referenced by b (i.e. point2). However, note that this change is only to Replace's local (stack) variables, and this change will only affect subsequent code in Replace (the dark blue line). It does NOT affect the calling function's variable references in any way, NOR does this change the point1 and point2 objects on the heap at all.
Pass by reference:
If however we we change the call to Replace<T>(ref T a, T b) and then change main to pass a by reference, i.e. Replace(ref a, b):
As before, two point objects allocated on the heap.
Now, when Replace(ref a, b) is called, while mains reference b (pointing to point2) is still copied during the call, a is now passed by reference, meaning that the "address" to main's a variable is passed to Replace.
Now when the assignment a = b is made ...
It is the the calling function, main's a variable reference which is now updated to reference point2. The change made by the re-assignment to a is now seen by both main and Replace. There are now no references to point1
Changes to (heap allocated) object instances are seen by all code referencing the object
In both scenarios above, no changes were actually made to the heap objects, point1 and point2, it was only local variable references which were passed and re-assigned.
However, if any changes were actually made to the heap objects point1 and point2, then all variable references to these objects would see these changes.
So, for example:
void main()
{
MyPoint a = new MyPoint { x = 1, y = 2 }; // point1
MyPoint b = new MyPoint { x = 3, y = 4 }; // point2
// Passed by value, but the properties x and y are being changed
DoSomething(a, b);
// a and b have been changed!
Assert.AreEqual(53, a.x);
Assert.AreEqual(21, b.y);
}
public void DoSomething(MyPoint a, MyPoint b)
{
a.x = 53;
b.y = 21;
}
Now, when execution returns to main, all references to point1 and point2, including main's variables a and b, which will now 'see' the changes when they next read the values for x and y of the points. You will also note that the variables a and b were still passed by value to DoSomething.
Changes to value types affect the local copy only
Value types (primitives like System.Int32, System.Double) and structs (like System.DateTime, or your own structs) are allocated on the stack, not the heap, and are copied verbatim onto the stack when passed into a call. This leads to a major difference in behaviour, since changes made by the called function to a value type field or property will only be observed locally by the called function, because it only will be mutating the local copy of the value type.
e.g. Consider the following code with an instance of the mutable struct, System.Drawing.Rectangle
public void SomeFunc(System.Drawing.Rectangle aRectangle)
{
// Only the local SomeFunc copy of aRectangle is changed:
aRectangle.X = 99;
// Passes - the changes last for the scope of the copied variable
Assert.AreEqual(99, aRectangle.X);
} // The copy aRectangle will be lost when the stack is popped.
// Which when called:
var myRectangle = new System.Drawing.Rectangle(10, 10, 20, 20);
// A copy of `myRectangle` is passed on the stack
SomeFunc(myRectangle);
// Test passes - the caller's struct has NOT been modified
Assert.AreEqual(10, myRectangle.X);
The above can be quite confusing and highlights why it is good practice to create your own custom structs as immutable.
The ref keyword works similarly to allow value type variables to be passed by reference, viz that the 'address' of the caller's value type variable is passed onto the stack, and assignment of the caller's assigned variable is now directly possible.
C# is actually pass by value. You get the illusion it's pass by reference, because when you pass a reference type you get a copy of the reference (the reference was passed by value). However, since your replace method is replacing that reference copy with another reference, it's effectively doing nothing (The copied reference goes out of scope immediately). You can actually pass by reference by adding the ref keyword:
void Replace<T>(ref T a, T b)
{
a = b;
}
This will get you your desired result, but in practice is a little strange.
In C# all the params that you pass to a method are passed by value.
Now before you shout keep on reading:
A value-type's value is the data that is copied while a reference type's value is actually a reference.
So when you pass an objects reference to a method and change that object then the changes will reflect outside the method as well since you are manipulating the same memory the object was allocated.
public void Func(Point p){p.x = 4;}
Point p = new Point {x=3,y=4};
Func(p);
// p.x = 4, p.y = 4
Now Lets look at this method:
public void Func2(Point p){
p = new Point{x=5,y=5};
}
Func2(p);
// p.x = 4, p.y = 4
So no changed occurred here and why? Your method simply created a new Point and changed p's reference(Which was passed by value) and therefore the change was local. You didn't manipulate the point, you changed the reference and you did locally.
And there comes the ref keyword that saves the day:
public void Func3(ref Point p){
p = new Point{x=5,y=5};
}
Func3(ref p);
// p.x = 5, p.y = 5
The same occurred in your example. You assigned a point with a new reference, but you did it locally.
C# is passing reference types objects not by reference, but rather it's passing the reference by value. Meaning you can mess around with their insides, but you can't change the assignment itself.
Read this great piece by Jon Skeet for deeper understanding.
Have a look on behavior by a simple program in C#:
class Program
{
static int intData = 0;
static string stringData = string.Empty;
public static void CallByValueForValueType(int data)
{
data = data + 5;
}
public static void CallByValueForRefrenceType(string data)
{
data = data + "Changes";
}
public static void CallByRefrenceForValueType(ref int data)
{
data = data + 5;
}
public static void CallByRefrenceForRefrenceType(ref string data)
{
data = data +"Changes";
}
static void Main(string[] args)
{
intData = 0;
CallByValueForValueType(intData);
Console.WriteLine($"CallByValueForValueType : {intData}");
stringData = string.Empty;
CallByValueForRefrenceType(stringData);
Console.WriteLine($"CallByValueForRefrenceType : {stringData}");
intData = 0;
CallByRefrenceForValueType(ref intData);
Console.WriteLine($"CallByRefrenceForValueType : {intData}");
stringData = string.Empty;
CallByRefrenceForRefrenceType(ref stringData);
Console.WriteLine($"CallByRefrenceForRefrenceType : {stringData}");
Console.ReadLine();
}
}
Output:
You're not understanding what passing by reference means. Your Replace method is creating a copy of the Point object--passing by value (which is actually the better way of doing it).
To pass by reference, so that a and b both reference the same point in memory, you need add "ref" to the signature.
You dont get it right.
It is similar like Java - everything is passed by value! But you do have to know, what the value is.
In primitive data types, the value is the number itself. In other cases it is reference.
BUT, if you copy reference to another variable, it holds same reference, but does not reference the variable (thus it is not pass by reference known in C++).
By default c# passes ALL arguements by value... that is why a and b remain unaffected in global scope in your examples. Here's a reference for those down voters.
To add more detail...in .NET, C# methods, using the default "pass by value" assigned to all parameters, reference types act differently in two scenarios. In the case of all reference types using classes (System.Object types), a copy of the "pointer" (to a memory block) to the original class or object is passed in and assigned to the method's parameter or variable name. This pointer is a value, too, and copied on the stack in memory where all value types are store. The value of the object isn't stored just a copy of its pointer, that points back to the original cl;ass object. I believe this is a 4-byte value. That's what is physically passed and stored in methods for all reference types. So, you now have a new method parameter or variable with a pointer assigned to it still pointing back to the original class object outside the method. You can now do two things to the new variable with the copied pointer value:
You can change the ORIGINAL object outside the method by changing its properties iniside your method. If "MyObject" is your variable with the copied pointer, you would do MyObject.myproperty = 6;, which changed the "myproperty" inside the original object outside the method. You did this as you passed in a pointer to the original object and assigned it to a new variable in your method. Note that this DOES change the referenced object outside the method.
Or, setting your variable with copied pointer to a new object and new pointer like so: MyObject = new SomeObject(); Here, we destroyed the old copied pointer assigned the variable above and assigned it to a new pointer to a new object! Now we have lost connection to the outside object and only changing a new object.

Why C# local variable should be assigned directly, even if it's default value?

If you look at next example:
public void TestLocalValuesAssignment()
{
int valueVariable; // = default(int) suits fine
string refType; // null suits fine as well
try
{
valueVariable = 5;
refType = "test";
}
catch (Exception){}
Console.WriteLine("int value is {0}", valueVariable);
Console.WriteLine("String is {0}", refType);
}
you could easily see, that variables valueVariable and refType could be unassigned before their usage in Console.WriteLine(). Compiler tells us about that with errors:
Error 1 Use of unassigned local variable 'valueVariable'
Error 2 Use of unassigned local variable 'refType'
This is a widespread case and there are loads of answers on how to fix that (possible fixes commented).
What I can't understand is why such behavior exists? How here local variables are different from class fields, where last ones get default value if not assigned (null for reference types and correspondent default value for value types)? Maybe there's an example or a corner case that explains why such compiler behavior is chosen?
basically - this is what MS decided.
If you want more you can read here and check Eric Lippert’s Blog
The reason this is illegal in C# is because using an unassigned local has high likelihood of being a bug.
It's described in c# spec:
5.1.7 Local variables
A local variable introduced by a local-variable-declaration is not
automatically initialized and thus has no default value. For the
purpose of definite assignment checking, a local variable introduced
by a local-variable-declaration is considered initially unassigned. A
local-variable-declaration may include a local-variable-initializer,
in which case the variable is considered definitely assigned only
after the initializing expression (§5.3.3.4).
Within the scope of a local variable introduced by a
local-variable-declaration, it is a compile-time error to refer to
that local variable in a textual position that precedes its
local-variable-declarator. If the local variable declaration is
implicit (§8.5.1), it is also an error to refer to the variable within
its local-variable-declarator.
When you do something that appears stupid, like reading from a variable you've never assigned, there are basically two things the compiler can do:
Give you a diagnostic calling your attention to what likely is a mistake.
Do something arbitrary.
Since option #1 helps you find mistakes, it is preferred, especially when the workaround to tell the compiler "No, I mean to use the original default value" is as simple as adding = 0, = null or = default(T).
As for why class members don't work the same way, it's because this can't be checked at compile time (because of the myriad different orders that the different methods could be called). There would be runtime cost of flags whether each member had been assigned, and testing of those flags.
Note that the compiler does enforce the restriction on struct members in a way that's easy to check at compile-time. Namely, each constructor is required to assign every member.
In reality, your code should be fine, but by strict interpretation, there is a code path which can leave your variables unassigned before use. The try block introduces the potential for code within the block to not be executed (if an exception is thrown), but still execute the code beyond the catch (because there is nothing in the catch such as return or throw to prevent the rest of your method from executing if an exception is thrown in the try).
If you are referring to the difference between initializing "struct" fields and initializing class fields, eg:
public class A
{
}
MyMethod()
{
int myInt; // Initialized to zero, yes, but not yet assigned.
// An error to use this before assigning it.
A myA; // defaults to null, which may be a valid initial state, but still unassigned.
// Also an error to use this before assigning it.
A oneMoreA = null; // Same value as default, but at least intention is clear.
A anotherA = new A(); // What is or is not happening in the constructor is a separate issue.
// At least anotherA refers to an actual instance of the class.

Categories

Resources