C # referans tipi değişkenler için "ref" kullanımı nedir?


176

Bir değer türü ( int, structvb.) Parametre olarak ( refanahtar sözcük olmadan ) iletirsem, bu değişkenin bir kopyasının yönteme geçtiğini, ancak refanahtar kelimeyi kullanırsam bu değişkene bir başvuru geçtiğini anlıyorum , yeni bir tane değil.

Ancak sınıflar gibi başvuru türlerinde, refanahtar sözcük olmadan bile , bir kopyaya değil, yönteme bir başvuru iletilir. Peki, refanahtar kelime ile referans türlerinin kullanımı nedir?


Örnek verelim:

var x = new Foo();

Aşağıdakiler arasındaki fark nedir?

void Bar(Foo y) {
    y.Name = "2";
}

ve

void Bar(ref Foo y) {
    y.Name = "2";
}

Yanıtlar:


154

Kullanacağınız foonoktaları değiştirebilirsiniz y:

Foo foo = new Foo("1");

void Bar(ref Foo y)
{
    y = new Foo("2");
}

Bar(ref foo);
// foo.Name == "2"

17
Böylece temel referansa bir referans alırsınız
lhahne

2
Orijinal referansın 'neyi ifade ettiğini' değiştirebilirsiniz, bu yüzden evet.
user7116

1
Chris, açıklaman harika! Bu konsepti anlamama yardımcı olduğunuz için teşekkürler.
Andreas Grech

4
Yani bir nesnede 'ref' kullanmak C ++ 'da çift işaretçiler kullanmak gibidir?
Tom Hazel

1
@TomHazel: -ish , bir işaretçinin işaret ettiği şeyi değiştirmek için C ++ 'da "çift" işaretçiler kullanmanız koşuluyla.
user7116

29

Gerçek referansı değiştirmek istediğiniz nesneler değil, işaret eden nesneler vardır:

void Swap<T>(ref T x, ref T y) {
    T t = x;
    x = y;
    y = t;
}

var test = new[] { "0", "1" };
Swap(ref test[0], ref test[1]);

21

Jon Skeet , C # 'da parametre geçişi hakkında harika bir makale yazdı . Geçiş parametrelerinin değere, başvuruya ( ref) ve çıktıya ( out) göre tam davranışını ve kullanımını açıkça detaylandırır .

İşte o sayfadan refparametrelerle ilgili önemli bir alıntı :

Referans parametreleri, işlev üyesi çağrısında kullanılan değişkenlerin değerlerini iletmez - değişkenleri kendileri kullanırlar. İşlev üyesi bildiriminde değişken için yeni bir depolama yeri oluşturmak yerine, aynı depolama yeri kullanılır, böylece işlev üyesindeki değişkenin değeri ve referans parametresinin değeri her zaman aynı olacaktır. Referans parametrelerin hem bildirim hem de çağırma işleminin bir parçası olarak ref değiştiricisine ihtiyacı vardır - bu, referans yoluyla bir şey iletirken her zaman net olduğu anlamına gelir.


11
Referans değer değeri geçmesi için köpek tasmalarınızı arkadaşınıza geçirmenin benzetmesini seviyorum ... olsa da hızlı bir şekilde parçalanıyor, çünkü muhtemelen arkadaşınızın size geri dönmeden önce shitzu'nuzu bir doberman'a alıp almadığını fark edeceğinizi düşünüyorum tasma ;-)
corlettk

16

Burada çok güzel açıkladı: http://msdn.microsoft.com/en-us/library/s6938f28.aspx

Makaleden Özet:

Referans tipindeki bir değişken, verilerini doğrudan içermez; verilerine bir referans içerir. Bir başvuru türü parametresini değere göre ilettiğinizde, sınıf üyesi değeri gibi başvurunun işaret ettiği verileri değiştirmek mümkündür. Ancak, başvurunun değerini değiştiremezsiniz; yani, aynı başvuruyu yeni bir sınıfa bellek ayırmak ve bloğun dışında kalmasını sağlamak için kullanamazsınız. Bunu yapmak için, ref veya out anahtar sözcüğünü kullanarak parametreyi iletin.


4
Açıklama gerçekten çok güzel. Ancak, yalnızca bağlantı yanıtları SO'da önerilmez. Buradaki okuyuculara kolaylık sağlamak için makaleden bir özet ekledim.
Marcel

10

Ref anahtar sözcüğüyle bir başvuru türü ilettiğinizde, başvuruyu başvuru ile iletirsiniz ve çağırdığınız yöntem parametreye yeni bir değer atayabilir. Bu değişiklik, çağrı kapsamına yayılacaktır. Ref olmadan, referans değere göre iletilir ve bu gerçekleşmez.

C # 'da' ref 'ile çok benzer olan' out 'anahtar sözcüğüne sahiptir, ancak' ref 'ile argümanların yöntemi çağırmadan önce başlatılması ve' out 'ile alma yönteminde bir değer atamanız gerekir.


5

Aktarılan referansı değiştirmenize izin verir.

void Bar()
{
    var y = new Foo();
    Baz(ref y);
}

void Baz(ref Foo y)
{
    y.Name = "2";

    // Overwrite the reference
    y = new Foo();
}

Ayrıca kullanabilirsiniz dışarı referans geçirilen önemsediğiniz yoksa:

void Bar()
{
    var y = new Foo();
    Baz(out y);
}

void Baz(out Foo y)
{
    // Return a new reference
    y = new Foo();
}

4

Başka bir kod grubu

class O
{
    public int prop = 0;
}

class Program
{
    static void Main(string[] args)
    {
        O o1 = new O();
        o1.prop = 1;

        O o2 = new O();
        o2.prop = 2;

        o1modifier(o1);
        o2modifier(ref o2);

        Console.WriteLine("1 : " + o1.prop.ToString());
        Console.WriteLine("2 : " + o2.prop.ToString());
        Console.ReadLine();
    }

    static void o1modifier(O o)
    {
        o = new O();
        o.prop = 3;
    }

    static void o2modifier(ref O o)
    {
        o = new O();
        o.prop = 4;
    }
}

3

Mevcut cevaplara ek olarak:

İki yöntemin farkını istediğiniz gibi: refveya kullanırken co (ntra) varyansı yoktur out:

class Foo { }
class FooBar : Foo { }

static void Bar(Foo foo) { }
static void Bar(ref Foo foo) { foo = new Foo(); }

void Main()
{
    Foo foo = null;
    Bar(foo);           // OK
    Bar(ref foo);       // OK

    FooBar fooBar = null;
    Bar(fooBar);        // OK (covariance)
    Bar(ref fooBar);    // compile time error
}

1

Bir yöntemdeki bir parametre her zaman bir kopya geçiriyor gibi görünüyor, soru neyin bir kopyasıdır. Bir kopya, bir nesne için bir kopya oluşturucu tarafından yapılır ve tüm değişkenler C # 'da Nesne olduğundan, bunların hepsi için böyle olduğuna inanıyorum. Değişkenler (nesneler) bazı adreslerde yaşayan insanlar gibidir. Ya bu adreslerde yaşayan insanları değiştiririz ya da telefon rehberindeki bu adreslerde yaşayanlara daha fazla referans oluşturabiliriz (sığ kopyalar oluşturabiliriz). Dolayısıyla, birden fazla tanımlayıcı aynı adrese başvurabilir. Referans türleri daha fazla alan ister, bu nedenle yığındaki tanımlayıcılarına doğrudan bir okla bağlanan değer türlerinin aksine, öbekteki başka bir adres için değeri vardır (kalacak daha büyük bir alan). Bu alanın yığından alınması gerekiyor.

Değer türü: Gösterge (değeri içerir = yığın değerinin adresi) ----> Değer türünün değeri

Referans türü: Tanımlayıcı (değer = yığın değerinin adresini içerir) ----> (değer = yığın değerinin adresini içerir) ----> Yığın değeri (çoğunlukla diğer değerlere adresler içerir), farklı oklara yapışan daha fazla ok hayal edin [0], Array [1], array [2] için yol tarifi

Bir değeri değiştirmenin tek yolu okları takip etmektir. Bir ok kaybolur / değişirse değere ulaşılamaz.


-1

Referans Değişkenleri adresi bir yerden başka bir yere taşır, böylece herhangi bir yerde herhangi bir güncelleştirme REF'in kullanıldığı tüm yerler üzerine yansır. Referans değişkeni (405), yöntemde geçirilen referans değişkenine yeni bir bellek tahsis edilinceye kadar iyidir.

Yeni bellek tahsis edildiğinde (410), bu nesne (408) üzerindeki değer değişikliği her yeri yansıtmayacaktır. Bu ref için geliyor. Ref referans referansıdır, bu nedenle yeni bellek tahsis edildiğinde o konuma işaret ettiğinden haberdar olur, bu nedenle değer everyOne tarafından paylaşılabilir. Daha fazla netlik için görüntüyü görebilirsiniz.

Referans Değişkeninde Ref

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.