передать по ссылке без ключевого слова ref - PullRequest
4 голосов
/ 20 января 2010

Я не ветеран в программировании сокетов, поэтому, анализируя код, обнаруженный в API базы данных, я наткнулся на этот код

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

Я понимаю сдвиги битов, но я не понимаю, как переменная 'buffer' продолжает получать значение, когда в аргументах нет ref или не возвращено. битовые сдвиги как-то редактируют фактическое значение буфера?

Ответы [ 8 ]

20 голосов
/ 20 января 2010

Ваша путаница очень распространена. Важным моментом является понимание того, что «ссылочные типы» и «передача по ссылке» (ref клавиатура) полностью независимы . В данном конкретном случае, поскольку byte[] является ссылочным типом (как и все массивы), это означает, что объект не копируется при его передаче, следовательно, вы всегда ссылаетесь на один и тот же объект.

Я настоятельно рекомендую вам прочитать отличную статью Джона Скита о Передача параметров в C # , и все должно стать ясно ...

5 голосов
/ 20 января 2010

Поскольку массив не является типом значения, это ссылочный тип. Ссылка на местоположение в куче передается по значению.

2 голосов
/ 20 января 2010

Лучший способ думать об этом - думать о переменных . Переменные по определению места хранения . Каковы места хранения в вашей программе? Это:

  • формальные параметры i и bufOutputStream первого WriteInt.
  • буфер локальной переменной в первом WriteInt
  • элементы (из них «IntSize») массива, на который ссылается буфер, после его выделения.
  • формальные параметры i, byte_array и pos второго WriteInt

Место хранения byte_array и место хранения в буфере - это разные места хранения. Но место хранения byte_array содержит ссылку на тот же массив, на который ссылается место хранения буфера. Следовательно, buffer [0] и byte_array [0] ссылаются на одно и то же место хранения.

Просто подумайте о местах хранения, и все это будет иметь смысл.

2 голосов
/ 20 января 2010

Я думаю, что некоторые примеры могут показать вам различия между ссылочными типами и типами значений и между передачей по ссылке и передачей по значению:

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

}
1 голос
/ 20 января 2010

Массивы в .Net являются ссылочными типами.

Следовательно, ваша функция получает ссылку на объект массива по значению. Поскольку все еще существует только один экземпляр массива, функция может изменить экземпляр, и изменения будут видны вызывающей стороне.

Добавление ключевого слова ref заставит функцию получить ссылку на объект массива по ссылке и, следовательно, позволит функции изменить ссылку для ссылки на другой экземпляр массива.

Другими словами, ключевое слово ref позволит вам написать следующее:

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

Для демонстрации:

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.
1 голос
/ 20 января 2010

C # похож на Java в том, что переменные ссылочного типа на самом деле являются указателями. Вы всегда передаете по значению, но со ссылочными типами значением является местоположение объекта, а не сам объект. Ключевое слово ref для ссылочного типа передает указатель по ссылке, поэтому назначение параметра ref изменит объект, на который указывает аргумент, переданный в аргументе.

0 голосов
/ 20 января 2010

Как сказал Юрий Факторович, типы значений (например, int, char, bool ecc.) Передаются по умолчанию по значению (если вы не укажете ref )

все остальные типы (массивы и объекты) по умолчанию передаются по ссылке

в вашем примере, если вы измените значения внутри массива, это отразит изменения также вне метода, но вы не можете переназначить объект.

полная справка об этом находится в MSDN

0 голосов
/ 20 января 2010

byte_array является ссылочным типом.

...