pass by reference without the ref keyword
I'm not a veteran in socket progra开发者_如何学Cmming, so while analyzing code I found in a database API I came across this code
public static void WriteInt(int i, NetworkStream bufOutputStream)
{
byte[] buffer = new byte[IntSize];
WriteInt(i, buffer, 0);
bufOutputStream.Write(buffer, 0, buffer.Length);
}
public static void WriteInt(int i, byte[] byte_array, int pos)
{
byte_array[pos] =(byte)( 0xff & (i >> 24)); byte_array[pos+1] = (byte)(0xff & (i >> 16)); byte_array[pos+2] = (byte)(0xff & (i >> 8)); byte_array[pos+3] = (byte)(0xff & i);
}
I understand the bit-shifts what I don't understand is how the 'buffer' var keeps getting a value when no ref is in the args or no return is made. the bitshifts are somehow editing the actual value of buffer?
Your confusion is a very common one. The essential point is realising that "reference types" and "passing by refrence" (ref
keyboard) are totally independent. In this specific case, since byte[]
is a reference type (as are all arrays), it means the object is not copied when you pass it around, hence you are always referring to the same object.
I strongly recommend that you read Jon Skeet's excellent article on Parameter passing in C#, and all should become clear...
Because an array isn't a value type, it's a reference type. The reference to the location on the heap is passed by value.
I think some examples can show you differences between reference types and value types and between passing by reference and passing by value:
//Reference type
class Foo {
public int I { get; set; }
}
//Value type
struct Boo {
//I know, that mutable structures are evil, but it only an example
public int I { get; set; }
}
class Program
{
//Passing reference type by value
//We can change reference object (Foo::I can changed),
//but not reference itself (f must be the same reference
//to the same object)
static void ClassByValue1(Foo f) {
//
f.I++;
}
//Passing reference type by value
//Here I try to change reference itself,
//but it doesn't work!
static void ClassByValue2(Foo f) {
//But we can't change the reference itself
f = new Foo { I = f.I + 1 };
}
//Passing reference typ by reference
//Here we can change Foo object
//and reference itself (f may reference to another object)
static void ClassByReference(ref Foo f) {
f = new Foo { I = -1 };
}
//Passing value type by value
//We can't change Boo object
static void StructByValue(Boo b) {
b.I++;
}
//Passing value tye by reference
//We can change Boo object
static void StructByReference(ref Boo b) {
b.I++;
}
static void Main(string[] args)
{
Foo f = new Foo { I = 1 };
//Reference object passed by value.
//We can change reference object itself, but we can't change reference
ClassByValue1(f);
Debug.Assert(f.I == 2);
ClassByValue2(f);
//"f" still referenced to the same object!
Debug.Assert(f.I == 2);
ClassByReference(ref f);
//Now "f" referenced to newly created object.
//Passing by references allow change referenced itself,
//not only referenced object
Debug.Assert(f.I == -1);
Boo b = new Boo { I = 1 };
StructByValue(b);
//Value type passes by value "b" can't changed!
Debug.Assert(b.I == 1);
StructByReference(ref b);
//Value type passed by referenced.
//We can change value type object!
Debug.Assert(b.I == 2);
Console.ReadKey();
}
}
The best way to think about this is to think about the variables. Variables are by definition storage locations. What are the storage locations in your program? They are:
- the formal parameters i and bufOutputStream of the first WriteInt.
- the local variable buffer in the first WriteInt
- the elements ("IntSize" of them) of the array referred to by buffer, after it is allocated.
- the formal parameters i, byte_array and pos of the second WriteInt
The byte_array storage location and the buffer storage location are different storage locations. But the byte_array storage location contains a reference to the same array that the buffer storage location refers to. Therefore buffer[0] and byte_array[0] refer to the same storage location.
Just think about the storage locations, and it'll all make sense.
C# is like Java in that reference type variables are actually pointers. You always pass by value, but with reference types the value is the location of the object, rather than the object itself. The ref keyword on a reference type is passing the pointer by reference, so an assignment to a ref parameter will change what object the argument passed in points to.
Arrays in .Net are reference types.
Therefore, your function receives a reference to the array object by value. Since there is still only a single array instance, the function can modify the instance and the changes will be seen the caller.
Adding the ref
keyword would make the function receive a reference to the array object by reference, and would therefore allow the function to change the reference to refer to a different array instance.
In other words, the ref
keyword would allow you to write the following:
public static void WriteInt(int i, ref byte[] byte_array, int pos)
{
byte_array = new byte[0]; //In the caller, the array will now be empty.
}
To demonstrate:
void SetReference(ref byte[] arrayRef) { arrayRef = new byte[1]; }
void SetValue(byte[] arrayVal) { arrayVal[1] = 42; }
byte[] array = new byte[4];
byte[] sameArray = array; //sameArray refers to the same instance
sameArray[0] = 77; //Since it's the same instance, array[4] is also 77.
SetValue(array); //array[1] is 42.
//Since it refers to the same array, sameArray[1] is also 42.
SetReference(ref array); //sameArray now refers to a new array of length 1.
//array still refers to the original array.
byte_array is a reference type.
Like Yuriy Faktorovich said, value types (like int, char, bool ecc.) are passed by default by value (unless you specify ref)
all other types (arrays, and Objects) are passed by default by reference
in your example if u change the values inside the array it will reflect changes also outside the method but you cannot reassign the object.
a full reference about it is in MSDN
精彩评论