Could some one please explain, What happens when a reference type is defined inside the value type.
I write the following code:
namespace ClassInsideStruct
{
class ClassInsideStruct
{
static void Main(string[] args)
{
ValueType ObjVal = new ValueType(10);
ObjVal.Display();
ValueType.ReferenceType ObjValRef = new ValueType.ReferenceType(10);
ObjValRef.Display();
Test(ObjVal, ObjValRef);
ObjVal.Display();
ObjValRef.Display();
Console.ReadKey();
}
private static void Test(ValueType v, ValueType.ReferenceType r)
{
v.SValue = 50;
r.RValue = 50;
}
}
struct ValueType
{
int StructNum;
ReferenceType ObjRef;
public ValueType(int i)
{
StructNum = i;
ObjRef = new ReferenceType(i);
}
public int SValue
{
get { return StructNum; }
set
{
StructNum = value;
ObjRef.RValue = value;
}
}
public void Display()
{
Console.WriteLine("ValueType: " + StructNum);
Console.Write("ReferenceType Inside ValueType Instance: ");
ObjRef.Display();
}
public class ReferenceType
{
int ClassNum;
public ReferenceType(int i)
{
ClassNum = i;
}
public void Display()
{
Console.WriteLine("Reference Type: " + ClassNum);
}
public int RValue
{
get { return ClassNum; }
set { ClassNum = value; }
}
}
}
}
Which outputs:
ValueType: 10
ReferenceType Inside ValueType Instance: Reference Type: 10
Reference Type: 10
ValueType: 10
ReferenceType Inside ValueType Instance: Reference Type: 50
Reference Type: 50
I'm curious to know, after calling the method Test(ObjVal, ObjValRef), how the values of ReferenceType is changed to 50 which resides inside the ValueType whose value is not changed?
I don't know for sure, but the compiler probably separates the code into a separate class and then just enforces the rules required. When you use a value type, the value is copied every time it is passed into a method. The reference to a reference type will get copied, but it refers to the same object. This same reference object will get changed while the value type that was copied will get changed. The original that you passed in will not reflect the changes on the copy.
Because Reference Types are Reference Types and Value Types are Value Types. No matter where they Reside.
And also Value type is not changing neither it is changing the Reference its holding. Its the Reference Type that gets changed(Read my words carefully).
i.e the underlying data at that address gets changed. The reference held by value type is still the same.
Value inside value type is reference, that it is not changed. But value that is pointed by the reference could be easily changed.
Reference types are passed into methods as a pointer, so modifying contents will modify the same location in memory. Value types are passed into methods by sending the value on the call stack.
when programming, it's important to understand that calling a method that takes arguments implies/includes/is the same as assigning values to those arguments. plus:
static void Main(string[] args)
{
ValueType ObjVal = new ValueType(10);
ObjVal.Display();
ValueType.ReferenceType ObjValRef = new ValueType.ReferenceType(10);
ObjValRef.Display();
//call to Test(ObjVal, ObjValRef); replaced by the following 4 lines
ValueType v = ObjVal;
ReferenceType r = ObjValRef;
v.SValue = 50;
r.RValue = 50;
ObjVal.Display();
ObjValRef.Display();
Console.ReadKey();
}
should give the same result as your example above. when you declare ValueType v = ObjVal; you are making a copy of the actual struct object, which means that v is a separate object all together. so changing the values of it's members won't affect ObjVal.
however, ReferenceType r = ObjValRef; makes a copy of a reference. So now there are two references, ObjValRef and r, pointing to the same object. Namely the object created when calling new ValueType.ReferenceType(10);
so when changing members of the object pointed to by any of these two references, this object changes, regardless of which pointer is used to perform the change.
oh, by the by.. a reference is just an address of an object. often this is a 32 bit number, but this changes from language to language, and from processor to processor.
and changing the reference copy in itself, e.g. r = null; won't affect the "original" reference ObjValRef, since r is a copy of ObjValRef, and not ObjValRef itself. it just appears as though they are the same, since they both point to the same object.
you can think of the actual object as a place (a park or some famous building, maybe "white mountain park") and the references as street signs pointing to this place. there can be many street signs pointing to the same place, but this doesn't mean that there are many "white mountain park". and this is the difference between value types and reference types.
Related
I am new to C# and I have been messing around with 'ref', 'out' and pointers, and I have a general question about how 'ref' works, especially when using objects and not primitive types. Say this is my method:
public void foo(ref Point p) {
p.set(1,1); // the x/y values are updated without constructing a new Point
}
and a similar method:
public void bar(Point p) {
p.set(1,1); // the x/y values are updated without constructing a new Point
}
EDIT: Point is a class in both cases
Both work, but is one more cost effective than the other? I know in C++ if you pass in a pointer you are only giving the memory address; from my understanding of C#, you cannot pass in an Object* into a method because of the automatic garbage collection. Does 'ref' pin an object to a location? Also, if you pass in an object to a method, like 'bar' above, is it passing a copy of the object or is it passing a pointer/reference?
Clarification: In my book I have, it does say if you want a method to update a primitive, such as int, you need to use ref (out if it is not initialized) or a *. I was asking if the same holds true for objects, and if passing an object as a parameter rather than a ref to an object costs more.
If your type is a struct, ref is roughly equivalent to a pointer to that struct. No new instances are created here. The difference (from passing it without ref) is that you can now mutate the original struct instance contained in that variable.
If your type is a class, ref simply adds one more level of indirection. No new instances are created here either. The difference (from passing it without ref) is that you can now entirely replace (not just mutate) the original class instance referenced by that variable with something else.
Since no new instances are created in either case, the garbage collector probably won't care about this in any important way.
In fact, class is a reference type, it mean that a variable of a reference type hold a reference to it's data instead of holding is data directly like value type.
When you pass a variable of a reference type as method parameter, it pass the reference to that data, not the data itself. So if update some properties of your object, the update is reflected in the original variable, except if you reassign the parameter.
Example from MSDN :
class PassingRefByVal
{
static void Change(int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
}
static void Main()
{
int[] arr = {1, 4, 5};
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr [0]);
Change(arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr [0]);
}
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: 888
*/
Passing a variable of a reference type with the ref keyword will reflect any change to the original variable, even if you reassin the parameter.
Example from MSDN :
class PassingRefByRef
{
static void Change(ref int[] pArray)
{
// Both of the following changes will affect the original variables:
pArray[0] = 888;
pArray = new int[5] {-3, -1, -2, -3, -4};
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
}
static void Main()
{
int[] arr = {1, 4, 5};
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr[0]);
Change(ref arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr[0]);
}
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: -3
*/
MSDN documentation.
Quick distinction between a class vs. a struct:
A class is a reference type. When an object of the class is created,
the variable to which the object is assigned holds only a reference to
that memory. When the object reference is assigned to a new variable,
the new variable refers to the original object. Changes made through
one variable are reflected in the other variable because they both
refer to the same data.
A struct is a value type. When a struct is
created, the variable to which the struct is assigned holds the
struct's actual data. When the struct is assigned to a new variable,
it is copied. The new variable and the original variable therefore
contain two separate copies of the same data. Changes made to one copy
do not affect the other copy.
https://msdn.microsoft.com/en-us/library/ms173109.aspx
Your example is tricky because in c# Point is an immutable struct, not an object.
Hopefully this example will help show what happens with structs and objects with and without ref.
public static void StructTest()
{
var fooStruct = new MyStruct();
var barStruct = new MyStruct();
Console.WriteLine(fooStruct.Value); // prints 0
Console.WriteLine(barStruct.Value); // prints 0
fooStruct(ref fooStruct);
barStruct(barStruct);
// Struct value only changes when passed by reference.
Console.WriteLine(fooStruct.Value); // prints 1
Console.WriteLine(barStruct.Value); // prints 0
}
public void fooStruct(ref MyStruct m)
{
m.Value++;
}
public void barStruct(MyStruct m)
{
m.Value++;
}
public static void ObjectTest()
{
var fooObject = new MyObject();
var barObject = new MyObject();
Console.WriteLine(fooObject.Value); // prints 0
Console.WriteLine(barObject.Value); // prints 0
fooObject(ref fooObject);
barObject(barObject);
// Objects are automatically passed by reference. No difference.
Console.WriteLine(fooObject.Value); // prints 1
Console.WriteLine(barObject.Value); // prints 1
fooSetObjectToNull(ref fooObject);
barSetObjectToNull(barObject);
// Reference is actually a pointer to the variable that holds a reference to the object.
Console.WriteLine(fooObject == null); // prints true
Console.WriteLine(barObject == null); // prints false
}
public void fooObject(ref MyObject m)
{
m.Value++;
}
public void barObject(ref MyObject m)
{
m.Value++;
}
public void fooSetObjectToNull(ref MyObject m)
{
m = null;
}
public void barSetObjectToNull(MyObject m)
{
m = null;
}
I am having a problem were the reference to an object in a list is lost, this is how I elaborated my code :
PropertyObject[] myProperties = new PropertyObject[200];
var objParent = new Parent();
var objProperty = new PropertyObject();
myProperties[0] = objProperty;
objParent.property = myProperties[0];
Now when I modify objParent.property it does not modify the object in the myProperties array, any workaround? I need this so that I don't have to iterate over the array.
This is how I modify the object :
public void modifyObject(ref Parent objectToModify) {
objectToModify.property.isThisCrazy = true;
}
Then I just invoke the modifyObject method.
structs are meant to be immutable. Assinging a struct to another variable will cause the struct to be copied.
When assigning properties on the one instance, the properties of the other other instance of the struct aren't changed. Hence, you don't see updated in the other reference.
Sample code demonstrating the problem with structs:
struct X
{
public string Y { get; set; }
public X(string y) : this()
{
Y = y;
}
}
X x = new X("abc");
X x2 = x;
x2.Y = "def";
Console.WriteLine(x.Y);
Console.WriteLine(x2.Y);
With classes you'd expected x.Y and x2.Y to be the same, but not with structs.
You write that a "reference to an object" is lost, but a struct has no "reference" to it.
A struct has value-type semantics. So when you assign with =, a copy of the right-hand side is made. You do:
myProperties[0] = objProperty;
This copies the value, and puts a copy inside the 0th entry of the array.
If you later modify the "original" instance objProperty, that change will not be present in the copy held in the array.
This is not really an array issue. The same happens with all struct value assignments. For example:
var objProperty2 = objProperty;
If the original objProperty is mutated afterwards, the copied value objProperty2 will be unaffected. See for example C# Reference type assignment VS value type assignment.
Some people consider mutable structs evil.
I just did a little experiment to see if altering an un-boxed variable would propagate changes made to the original source and got two completely different results depending on the types that I used. I am mainly interested in figuring this out for WPF data binding applications in which I bind to objects, cast, change, and hope that the original's update their UIs.
My results were as follows.
Simple types seem to lose their reference to the original source after un-boxing.
Custom types seem to keep their reference.
It seems that I don't have anything to worry about in my scenario of hoping that my WPF UI updates itself after making changes to unboxed bound data contexts; however, not knowing WHY this happens only with complex objects worries me a bit. I do not want my UI to fail on rare or odd occasions I do not know about. Can anyone explain what the heck is mechanically going on back there?
class Program
{
//simple types
private static object sbox1;
private static object sbox2;
private static int svalue1 = 10;
private static int svalue2 = 15;
//custom types
private static MyType cvalue1;
private static MyType cvalue2;
private static object cbox1;
private static object cbox2;
static void Main(string[] args)
{
//Box up the values
sbox1 = svalue1;
sbox2 = svalue2;
//unbox the values to local var
var sunboxed1 = (int)sbox1;
var sunboxed2 = (int)sbox2;
//change the values in the new unboxed vars
sunboxed1 = -10;
sunboxed2 = -15;
//check unboxed values and check original value variables
Console.WriteLine("unboxed1 = " + sunboxed1);
Console.WriteLine("unboxed2 = " + sunboxed2);
Console.WriteLine("value1 = " + svalue1);
Console.WriteLine("value2 = " + svalue2);
//Now try hand at custom types
cvalue1 = new MyType() { Example = "I am cvalue1's original string." };
cvalue2 = new MyType() { Example = "I am cvalue2's original string." };
//now box them up.
cbox1 = cvalue1;
cbox2 = cvalue2;
//now unbox and change the strings
var cunboxed1 = cbox1 as MyType;
var cunboxed2 = cbox2 as MyType;
//change the original strings to see if they propogate to original objects
cunboxed1.Example = "I am cunboxed1's altered string.";
cunboxed2.Example = "I am cunboxed2's altered string.";
//print unboxed and originals values to compare
Console.WriteLine("cunboxed1.Example = " + cunboxed1.Example);
Console.WriteLine("cunboxed2.Example = " + cunboxed2.Example);
Console.WriteLine("cvalue1.Example = " + cvalue1.Example);
Console.WriteLine("cvalue2.Example = " + cvalue2.Example);
Console.ReadKey();
}
}
class MyType
{
public string Example { get; set; }
}
Results from the tester app:
unboxed1 = -10
unboxed2 = -15
value1 = 10
value2 = 15
cunboxed1.Example = I am cunboxed1's altered string.
cunboxed2.Example = I am cunboxed2's altered string.
cvalue1.Example = I am cunboxed1's altered string.
cvalue2.Example = I am cunboxed2's altered string.
What you're seeing is caused by the different handling of value and reference types. In the first example, you are boxing an int. This is wrapped in Int32, a value type, and assigned to your object variable. In your unboxing step, the original Int32 object is copied, because it is a value type, and its value is assigned to your int variable sunboxed1. sbox1 and sunboxed1 hold different values and exist at different memory locations, so no change made to one will affect the other.
In your second example, you are assigning a class to an object variable. This isn't boxing it; you're simply upcasting a reference to your object. When you subsequently downcast it back to MyType with the as keyword, you get a reference to the original object. So, cvalue1 and cunboxed1 hold a reference to the same object.
As dotnetom stated this is more about value vs reference types.
According to MSDN http://msdn.microsoft.com/en-us/library/t63sy5hs.aspx
A data type is a value type if it holds the data within its own memory allocation. Value types include the following:
All numeric data types....
However, for classes:
A reference type contains a pointer to another memory location that holds the data. Reference types include the following: Class types, such as Form.....
With the last line being the most interesting.
A class is a reference type. For this reason, reference types such as Object and String are supported by .NET Framework classes. Note that every array is a reference type, even if its members are value types.
This is why the custom types above changed the original values above after un-boxing. strings themselves are reference types so it it might seem that the reason changes were propogated was due to me using a string as an example and not a numeric type; however, changing the Example property to an int will still change the original source as well and yield the same results.
The string type in C# is a reference type, and passing a reference type argument by value copies the reference so that I don't need to use the ref modifier. However, I need to use the ref modifier for modifying the input string. Why is this?
using System;
class TestIt
{
static void Function(ref string input)
{
input = "modified";
}
static void Function2(int[] val) // Don't need ref for reference type
{
val[0] = 100;
}
static void Main()
{
string input = "original";
Console.WriteLine(input);
Function(ref input); // Need ref to modify the input
Console.WriteLine(input);
int[] val = new int[10];
val[0] = 1;
Function2(val);
Console.WriteLine(val[0]);
}
}
The reason you need to ref the string parameter is that even though you pass in a reference to a string object, assigning something else to the parameter will just replace the reference currently stored in the parameter variable. In other words, you have changed what the parameter refers to, but the original object is unchanged.
When you ref the parameter, you have told the function that the parameter is actually an alias for the passed-in variable, so assigning to it will have the desired effect.
EDIT: Note that while string is an immutable reference type, that's not too relevant here. Since you're just trying to assign a new object (in this case the string object "modified"), your approach wouldn't work with any reference type. For example, consider this slight modification to your code:
using System;
class TestIt
{
static void Function(ref string input)
{
input = "modified";
}
static void Function2(int[] val) // don't need ref for reference type
{
val = new int[10]; // Change: create and assign a new array to the parameter variable
val[0] = 100;
}
static void Main()
{
string input = "original";
Console.WriteLine(input);
Function(ref input); // need ref to modify the input
Console.WriteLine(input);
int[] val = new int[10];
val[0] = 1;
Function2(val);
Console.WriteLine(val[0]); // This line still prints 1, not 100!
}
}
Now, the array test "fails", because you're assigning a new object to the non-ref parameter variable.
It helps to compare string to a type that is like string but is mutable. Let's see a short example with StringBuilder:
public void Caller1()
{
var builder = new StringBuilder("input");
Console.WriteLine("Before: {0}", builder.ToString());
ChangeBuilder(builder);
Console.WriteLine("After: {0}", builder.ToString());
}
public void ChangeBuilder(StringBuilder builder)
{
builder.Clear();
builder.Append("output");
}
This produces:
Before: input
After: output
So we see that for a mutable type, i.e. a type that can have its value modified, it is possible to pass a reference to that type to a method like ChangeBuilder and not use ref or out and still have the value changed after we called it.
And notice that at no time did we actually set builder to a different value in ChangeBuilder.
By contrast, if we do the same thing with string:
public void Caller2()
{
var s = "input";
Console.WriteLine("Before: {0}", s);
TryToChangeString(s);
Console.WriteLine("After: {0}", s);
}
public void TryToChangeString(string s)
{
s = "output";
}
This produces:
Before: input
After: input
Why? Because in TryToChangeString we are not actually changing the contents of the string referenced by the variable s, we are replacing s with an entirely new string. Furthermore, s is a local variable to TryToChangeString and so replacing the value of s inside the function has no effect on the variable that was passed in to the function call.
Because a string is immutable, there is no way, without using ref or out, to affect the callers string.
Finally, the last example does what we want with string:
public void Caller3()
{
var s = "input";
Console.WriteLine("Before: {0}", s);
ChangeString(ref s);
Console.WriteLine("After: {0}", s);
}
public void ChangeString(ref string s)
{
s = "output";
}
This produces:
Before: input
After: output
The ref parameter actually makes the two s variables aliases for each other. It's as though they were the same variable.
Strings are immutable - you are not modifying the string but replacing the object the reference points to with another one.
Compare that with e.g., a List: To add Items, you don't need ref. To replace the entire list with a different object, you need ref (or out).
This is the case for all immutable types. string happens to be immutable.
In order to change the immutable type outside of the method, you must change the reference. Therefore either ref or out is required to have an effect outside of the method.
Note: It's worth noting that in your example, you are calling out a particular case that does not match the other example: you are actually pointing to a different reference rather than simply changing the existing reference. As noted by dlev (and the Skeet himself in my comments), if you did the same for all other types (e.g., val = new int[1]), including mutable ones, then you will "lose" your changes once the method returns because they did not happen to the same object in memory, unless you use ref or out like you did with string above.
To hopefully clarify:
You are passing in a pointer that points to your object in memory. Without ref or out, a new pointer is made that points to the exact same location, and all changes happen using the copied pointer. Using them, the same pointer is used and all changes made to the pointer are reflected outside the method.
If your object is mutable, then that means that it can be changed without creating a new instance of the object. If you create a new instance, then you must point to somewhere else in memory, which means you must change your pointer.
Now, if your object is immutable, then that means that it cannot be changed without creating a new instance.
In your example, you created a new instance of a string (equal to "modified") and then changed the pointer (input) to point to that new instance. For the int array, you changed one of the 10 values effectively pointed to by val, which does not require messing with val's pointer--it simply goes to where you want (the first element of the array), and then modifies that first value, in-place.
A more similar example would be (stolen from dlev, but this is how to make them truly comparable):
static void Function(ref string input)
{
input = "modified";
}
static void Function2(int[] val)
{
val = new int[1];
val[0] = 100;
}
Both functions change their parameter's pointer. Only because you used ref does input "remember" its changes, because when it changes the pointer, it is changing the pointer that was passed in and not just a copy of it.
val will still be an array of 10 ints outside of the function, and val[0] will still be 1 because the "val" within Function2 is a different pointer that originally points to the same location as Main's val, but it points somewhere else after the new array is created (the different pointer points to the new array, and the original pointer continues to point to the same location).
If I used ref with the int array, then it to would have changed. And it would have changed in size too.
A better example for newbies:
string a = "one";
string b = a;
string b = "two";
Console.WriteLine(a);
... will output "one".
Why? Because you are assigning a whole new string into pointer b.
The confusion is that ref type references are passed by value by default, to modify the reference itself (what the object points to) you have to pass the reference by reference - using ref.
In your case you are handling strings - assigning a string to a variable (or appending to, etc.) changes the reference, since strings are immutable there is no way to avoid this either, so you have to use ref.
You're right. Arrays and strings are reference types. But if to be honest and compare similar behavior you should write something like this:
static void Function2(int[] val) // It doesn't need 'ref' for a reference type
{
val = new[] { 1, 2, 3, 4 };
}
But in your example you perform a write operation in some element of a C# one-dimensional array via reference val.
When reference variable can be passed by reference :
class Example
{
public string str="Demo";
public int[] intValues={1,3,4,5};
public static void StrPassing(string someStr)
{
string otherStr="Changed!";
someStr=otherStr;
}
public static void NumPassing(int[] a)
{
a[2] = 115;
}
}
static void Main(string[] args)
{
Example ex = new Example();
Example.StrPassing(ex.str);
Example.NumPassing(ex.intValues);
foreach (int i in ex.intValues)
{
Console.WriteLine(i);
}
Console.WriteLine(ex.str);
Console.ReadLine();
}
the value of intValues[2] is changed as 115 as the reference is being passed.But the value of the string "str" (demo) is not changed to "Changed!".What is the reason for it?.can i take it as Arrays are passed by reference and other reference types are passed by
value?
Whatever you pass to a method as arguments is passed by value which, for reference types, means that a reference is passed by value. So you can't change the object to another one but you can surely change its contents (because that doesn't change the actual reference, just some memory elsewhere).
As your example with the array demonstrates you take the array reference (but don't change it) and change a value in the array. This is just like taking some object and changing a property value. You can do this just fine from within a method too.
If you want to change a string, which is an immutable object in .NET, then you need to resort to ref parameters:
public static void StrPassing(ref string someStr)
{
string otherStr="Changed!";
someStr=otherStr;
}
And call it like this:
string foo = "foo";
StrPassing(ref foo);
Console.WriteLine(foo); // should print "Changed!"
The ref keyword ensures that your method gets the actual reference to the string and can change it, instead of just a copy of the reference. So then you can replace the object by an entirely new one.
To come back to your array: You'd have a hard time too, to change the passed array to an entirely different array:
public static void NumPassing(int[] a)
{
a = new int[15];
}
wouldn't work too because then you'd try exactly the same as changing a string to an entirely different string.
You need to distinguish between changing which object a variable refers to and changing *the content of the object".
In this code:
public static void StrPassing(string someStr)
{
string otherStr="Changed!";
someStr=otherStr;
}
... you are changing the value of someStr. You're not making any change to the string that someStr originally refers to. Indeed, you can't because strings are immutable. (If it were a StringBuilder, you could set the length to 0 and then append "Changed!")
Changing the value of someStr has no effect because the argument (ex.str) was passed by value. (The value in question is a reference, but that doesn't mean it's passed by reference.)
Now compare that with this code:
public static void NumPassing(int[] a)
{
a[2] = 115;
}
Here you're not changing the value of a - you're changing the contents of the array that a refers to.
In short, unless you use ref/out, arguments will be passed by value - but for reference types that value is just a reference.
I have an article on parameter passing which goes into all of this in a lot more detail.
What you'd need to do is change the signature for StrPassing to look like this:
public static void StrPassing(ref string someStr)
Strings are special in C#. They are immutable reference types which makes they exhibit similar behavior as value types.
Here's a good discussion.