Related
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.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
To give context to the code imagine a collection of objects inside a cube. The objects are placed randomly and can affect each other. Several series of test events are planned then executed against the cube of objects. Only the best result is kept. This is not the real problem but a simplified version to focus the question.
Sample code
class Loc{
double UpDown
double LeftRight
double FrontBack
}
class Affects{
string affectKey
List<string> impacts //scripts that execute against properties
}
class Item{
Loc startLoc
Loc endLoc
List<string> affectedBy
string resultText // summary of analysis of changes
}
class ItemColl{
List<Item> myItems
}
class main{
ItemColl items
List<string> actions
void ProcessAffects(ItemColl tgt, List<string> acts){
// take actions against the tgt set and return
}
int IsBetter(ItemColl orig, List<Items> altered){
// compares the collection to determine "better one"
// positive better, negative worse, zero for no change
}
void DoThings(){
// original code
ItemColl temp = items
ProcessAffects(temp,actions)
IsBetter(temp,actions)
// the result was always zero - admittedly a duh error
}
}
When I added an alternate constructor that copied the object passed in and did the same to all subordinate objects, as in
class ItemColl{
public ItemColl(){}
public ItemColl (ItemColl clone){
// do a deep copy
}
// partial code from main DoThings
// replaced ItemColl temp = items
// with
ItemColl temp = new ItemColl(items)
it solved the problem that lead me to first question. (Thanks to the people who answered that question kindly.) What I am stuck on is whether or not there are other options to consider? I am hoping this restatement has a better focus and if I am not taking advantage of some newer efficiencies I would like to know.
I removed the old question entirely and re-phrased post face-palm.
Before you get into parameters, you need some background:
Background
There are two kinds of objects in .NET-land, Reference types and Value types. The main difference between the two is how assignment works.
Value Types
When you assign a value type instance to a variable, the value is copied to the variable. The basic numeric types (int, float, double, etc) are all value types. As a result, in this code:
decimal dec1 = 5.44m;
decimal dec2 = dec1;
dec1 = 3.1415m;
both decimal variables (dec and dec2) are wide enough to hold a decimal valued number. In each case, the value is copied. At the end, dec1 == 3.145m and dec2 == 5.44m.
Nearly all value types are declared as a struct (yes, if you get access to the .NET sources, int is a struct). Like all .NET types, they act when boxed as if they are derived from the object base class (their derivation is through System.ValueType. Both object (aka System.Object) and System.ValueType are reference types, even though the unboxed types that derive from System.ValueType are value types (a little magic happens here).
All value types are sealed/final - you can't sub-class them. You also can't create a default constructor for them - they come with a default constructor that initializes them to their default value. You can create additional constructors (which don't hide the built-in default constructor).
All enums are value types as well. They inherit from System.Enum but are value types and behave mostly like other value types.
In general, value types should be designed to be immutable; not all are.
Reference Types
Variables of reference types hold references, not values. That said, it sometimes help to think of them holding a value - it's just that that value is a reference to an object on the managed heap.
When you assign to a variable of reference type, you are assigning the reference. For example:
public class MyType {
public int TheValue { get; set; }
// more properties, fields, methods...
}
MyType mt1 = new MyType() {TheValue = 5};
MyType mt2 = mt1;
mt1.TheValue = 42;
Here, the mt1 and mt2 variables both contain references to the same object. When that object is mutated in the final line of code, you end up with two variables both referring to an object whose TheValue property is 42.
All types declared as a class are reference types. In general, other than the numeric types, enums and bools, most (but not all) of the types that you normally encounter will be reference types.
Anything declared to be a delegate or an event are also reference types under the covers. Someone mentioned interface. There is no such thing as an object typed purely as an interface. Both structs and classes may be declared to implement an interface - it doesn't change their value/reference type nature, but a struct stored as an interface will be boxed.
Difference in Constructor Behavior
One other difference between Reference and Value Types is what the new keyword means when constructing a new object. Consider this class and this struct:
public class CPoint {
public float X { get; set; }
public float Y { get; set; }
public CPoint (float x, float y) {
X = x;
Y = y;
}
}
public struct SPoint {
public float X { get; set; }
public float Y { get; set; }
public CPoint (float x, float y) {
X = x;
Y = y;
}
}
They are basically the same, except that CPoint is a class (a reference type) and SPoint is a struct (a value type).
When you create an instance of SPoint using the two float constructor (remember, it gets a default constructor auto-magically), like this:
var sp = new SPoint (42.0, 3.14);
What happens is that the constructor runs and creates a value. That value is then copied into the sp variable (which is of type SPoint and large enough to hold a two-float SPoint).
If I do this:
var cp = new CPoint (42.0, 3.14);
Something very different happens. First, memory is allocated on the managed heap large enough to hold a CPoint (i.e., enough to hold two floats plus the overhead of the object being a reference type). Then the two-float constructor runs (and that constructor is the only constructor - there is no default constructor (the additional, programmer-written constructor hides the compiler generated default constructor)). The constructor initializes that newCPoint in the memory allocated on the managed heap. Finally, a reference to that newly create object is created and copied to the variable cp.
Parameter Passing
Sorry the preamble took so long.
Unless otherwise specified, all parameters to functions/methods are passed by value. But, don't forget that the value of a variable of reference type is a reference.
So, if I have a function declared as (MyType is the class declared above):
public void MyFunction(decimal decValue, MyType myObject) {
// some code goes here
}
and some code that looks like:
decimal dec1 = 5.44m;
MyType mt1 = new MyType() {TheValue = 5};
MyFunction (dec1, mt1);
What happens is that the value of dec1 is copied to the function parameter (decValue) and available for use within MyFunction. If someone changes the value of the decValue within the function, no side effects outside the function occurs.
Similarly, but differently, the value of mt1 is copied to the method parameter myObject. However, that value is reference to a MyType object residing on the managed heap. If, within the method, some code mutates that object (say: myObject.TheValue=666;), then the object to which both the mt1 and myObject variables refer is mutated, and that results in a side effect viewable outside of the function. That said, everything is still being passed by value.
Passing Parameters by Reference
You can pass parameters by reference in two ways, using either the out or ref keywords. An out parameter does not need to be initialized before the function call (while a ref parameter must be). Within the function, an out parameter must be initialized before the function returns - ref parameters may be initialized, but they do not need to be. The idea is that ref parameters expect to pass in and out of the function (by reference). But out parameters are designed simply as a way to pass something out of the function (by reference).
If I declare a function like:
public void MyByRefFunction(out decimal decValue, ref MyType myObject) {
decValue = 25.624; //decValue must be intialized - it's an out parameter
myObject = new MyType (){TheValue = myObject.TheValue + 2};
}
and then I call it this way
decimal dec1; //note that it's not initalized
MyType mt1 = new MyType() {TheValue = 5};
MyType mt2 = mt1;
MyByRefFunction (out dec1, ref mt1);
After that call, dec1 will contain the value 25.624; that value was passed out of the function by reference.
Passing reference type variables by reference is more interesting. After the function call, mt1 will no longer refer to the object created with TheValue equal to 5, it will refer to the newly created object with TheValue equal to 5 + 2 (the object created within the function). Now, mt1 and mt2 will refer to different object with different TheValue property values.
With reference types, when you pass a variable normally, the object you pass it may mutate (and that mutation is visible after the function returns). If you pass a reference by reference, the reference itself may mutate, and the value of the reference may be different after the function returns.
All custom objects (derived from tobject) are "Reference type".
Nope. See the docs pages for Reference Types and Value Types
The following keywords are used to declare reference types:
class
interface
delegate
C# also provides the following built-in reference types:
dynamic
object
string
A value type can be one of the two following kinds:
a structure type ...
an enumeration type ...
So any time you make a class, it's always a Reference type.
EVERY type inherits from Object - Value Types and Reference Types.
Even if you pass it to a function with a reference parameter, as with the RefChange function both items are changed and both have exactly the same values in the integer list.
The ref keyword just forces your parameter to be passed by reference. Using ref with a Reference Type allows you to reassign the original passed in reference. See What is the use of “ref” for reference-type variables in C#?
.
Do not confuse the concept of passing by reference with the concept of reference types. The two concepts are not the same. A method parameter can be modified by ref regardless of whether it is a value type or a reference type. There is no boxing of a value type when it is passed by reference.
Source
Of course, the ref keyword is important when you pass in a Value Type, such as a struct.
If you want to pass a copy of an object, create an overloaded constructor to which you pass the original object and inside the constructor manage the duplication of the values that matter.
That's called a Copy Constructor, and is a long-established pattern, if you want to use it. In fact, there is a new c# 9.0 feature all about it: records.
well i cant comment since my reputation is too low, but value types are usually in built types such as int, float ...
everything else is reference type. reference type is always a shallow copy regardless of ref keyword.
ref keyword mainly served for value-type or act as a safeguard.
if u want to deep copy, Icloneable is very useful.
Technically what are the meanings and differences of the terms declaring, instantiating, initializing and assigning an object in C#?
I think I know the meaning of assigning but I have no formal definition.
In msdn, it is said "the act of creating an object is called instantiation". But the meaning creating seems vague to me. You can write
int a;
is a then created?
Declaring - Declaring a variable means to introduce a new variable to the program. You define its type and its name.
int a; //a is declared
Instantiate - Instantiating a class means to create a new instance of the class. Source.
MyObject x = new MyObject(); //we are making a new instance of the class MyObject
Initialize - To initialize a variable means to assign it an initial value.
int a; //a is declared
int a = 0; //a is declared AND initialized to 0
MyObject x = new MyObject(); //x is declared and initialized with an instance of MyObject
Assigning - Assigning to a variable means to provide the variable with a value.
int a = 0; //we are assigning to a; yes, initializing a variable means you assign it a value, so they do overlap!
a = 1; //we are assigning to a
In general:
Declare means to tell the compiler that something exists, so that space may be allocated for it. This is separate from defining or initializing something in that it does not necessarily say what "value" the thing has, only that it exists. In C/C++ there is a strong distinction between declaring and defining. In C# there is much less of a distinction, though the terms can still be used similarly.
Instantiate literally means "to create an instance of". In programming, this generally means to create an instance of an object (generally on "the heap"). This is done via the new keyword in most languages. ie: new object();. Most of the time you will also save a reference to the object. ie: object myObject = new object();.
Initialize means to give an initial value to. In some languages, if you don't initialize a variable it will have arbitrary (dirty/garbage) data in it. In C# it is actually a compile-time error to read from an uninitialized variable.
Assigning is simply the storing of one value to a variable. x = 5 assigns the value 5 to the variable x. In some languages, assignment cannot be combined with declaration, but in C# it can be: int x = 5;.
Note that the statement object myObject = new object(); combines all four of these.
new object() instantiates a new object object, returning a reference to it.
object myObject declares a new object reference.
= initializes the reference variable by assigning the value of the reference to it.
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.
Passing Value Type parameters to functions in c# is by value unless you use the ref or out keyword on the parameter. But does this also apply to Reference Types?
Specifically I have a function that takes an IList<Foo>. Will the list passed to my function be a copy of the list with copy of its contained objects? Or will modifications to the list also apply for the caller? If so - Is there a clever way I can go about passing a copy?
public void SomeFunction()
{
IList<Foo> list = new List<Foo>();
list.Add(new Foo());
DoSomethingWithCopyOfTheList(list);
..
}
public void DoSomethingWithCopyOfTheList(IList<Foo> list)
{
// Do something
}
All parameters are passed by value unless you explicitly use ref or out. However, when you pass an instance of a reference type, you pass the reference by value. I.e. the reference itself is copied, but since it is still pointing to the same instance, you can still modify the instance through this reference. I.e. the instance is not copied. The reference is.
If you want to make a copy of the list itself, List<T> has a handy constructor, that takes an IEnumerable<T>.
You're not alone; this confuses a lot of people.
Here's how I like to think of it.
A variable is a storage location.
A variable can store something of a particular type.
There are two kinds of types: value types and reference types.
The value of a variable of reference type is a reference to an object of that type.
The value of a variable of value type is an object of that type.
A formal parameter is a kind of variable.
There are three kinds of formal parameters: value parameters, ref parameters, and out parameters.
When you use a variable as an argument corresponding to a value parameter, the value of the variable is copied into the storage associated with the formal parameter. If the variable is of value type, then a copy of the value is made. If the variable is of reference type, then a copy of the reference is made, and the two variables now refer to the same object. Either way, a copy of the value of the variable is made.
When you use a variable as an argument corresponding to an out or ref parameter the parameter becomes an alias for the variable. When you say:
void M(ref int x) { ...}
...
int y = 123;
M(ref y);
what you are saying is "x and y now are the same variable". They both refer to the same storage location.
I find that much easier to comprehend than thinking about how the alias is actually implemented -- by passing the managed address of the variable to the formal parameter.
Is that clear?
The list is passed by reference, so if you modify the list in SomeFunction, you modify the list for the caller as well.
You can create a copy of a list by creating a new one:
var newList = new List<Foo>(oldList);
your list is passed by reference. If you want to pass a copy of the list you can do:
IList<Foo> clone = new List<Foo>(list);
if you add/remove elements in clone it won't modify list
but the modifications of the elements themselves will be taken into account in both lists.
When you pass reference type by value (without ref or out keywords) you may modify this reference type inside this method and all changes will reflect to callers code.
To solve your problem you may explicitly create a copy and pass this copy to your function, or you may use:
list.AsReadOnly();
When passing reference types, you pass the reference. This is an important concept.
If you pass a reference
byref, you pass the reference (pointer) directly.
byval, you pass a copy of the reference (pointer).
A reference is not the instance referenced. A reference is analagous to a pointer.
To pass a copy of the instance of a referencetype, you first must make a copy yourself and pass a reference to the copy. As such then you will not be modifying the original instance.