views:

247

answers:

8

I'm not a veteran in socket programming, 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?

+4  A: 

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.

Yuriy Faktorovich
This doesn't really address the confusion that jtzero has about the use of ref.
Sean Nyman
byte[] is a subclass of System.Array, which is a reference type.
Will
@sean Reference types are always passed by reference, value types are always passed by value... Unless you use `ref` or `out`, hence the reason behind the existence of those keywords. Adding them to a reference argument is like an unsharpened pencil.
Will
This is incorrect. Do not confuse reference types with passing by reference... they are totally independent.
Noldorin
@Yuriy: That's right now...
Noldorin
@Noldorin: I'm calling it a wording issue.
Yuriy Faktorovich
@Yuriy: Hehe, if you say so...
Noldorin
A: 

byte_array is a reference type.

Ariel
+1  A: 

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.

Sean Nyman
+1  A: 

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.
SLaks
A: 

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

+7  A: 

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...

Noldorin
simple yet effective, thank you.
jtzero
+2  A: 

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();
    }

}
Sergey Teplyakov
A: 

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.

Eric Lippert