Ref tarafından geçen liste - bu davranışı açıklamama yardım edin


110

Aşağıdaki programa bir göz atın:

class Test
{
    List<int> myList = new List<int>();

    public void TestMethod()
    {
        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList(myList);

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList(List<int> myList)
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}

Ben kabul myListgeçebilirdi refve çıkış alacaktı

3
4

Liste gerçekten "ref tarafından aktarılır", ancak yalnızca sortişlev etkili olur. Aşağıdaki ifadenin myList = myList2;hiçbir etkisi yoktur.

Yani çıktı aslında:

10
50
100

Bu davranışı açıklamama yardım eder misin? Gerçekten Eğer myListedilmez geçti-by-ref (ondan göründüğü gibi myList = myList2etkili olmuyor), nasıl myList.Sort()etkinlesmesi?

Bu ifadenin bile etkili olmayacağını ve çıktının şöyle olacağını varsayıyordum:

100
50
10

Sadece bir gözlem (ve sorunun burada basitleştirildiğinin farkındayım), ancak aslında yeni bir liste oluşturuyorsa, ChangeLista List<int>olmaktan çok a döndürmek en iyisi gibi görünüyor void.
Jeff B

Yanıtlar:


111

Bir geçiyoruz listeye referansı , ama senin değildir liste değişkeni geçirerek referans olarak - aramak o kadar zaman değişkenin değerini (yani referans - düşünmek "işaretçi") kopyalanır - ve değişiklikleri için değeri parametre içinde değildir tarafından görülen .ChangeListChangeList TestMethod

Deneyin:

private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);

Bu daha sonra yerel değişkene myRef (içinde belirtildiği gibi TestMethod) bir başvuru iletir ; şimdi, içindeki parametreyi yeniden ChangeListatarsanız, içindeki değişkeni de yeniden atarsınız TestMethod.


Aslında bunu yapabilirim, ancak sıralamanın nasıl etkili olduğunu bilmek istiyorum
nmdr

6
@Ngm - aradığınızda ChangeList, sadece referans kopyalanır - aynı nesnedir. Nesneyi bir şekilde değiştirirseniz, o nesneye referansı olan her şey değişikliği görecektir.
Marc Gravell

225

Başlangıçta aşağıdaki gibi grafiksel olarak gösterilebilir:

Init durumları

Ardından sıralama uygulanır myList.Sort(); Koleksiyonu sırala

Son olarak, bunu yaptığınızda: myList' = myList2referanslardan birini kaybettiniz, ancak orijinali kaybettiniz ve koleksiyon sıralanmış halde kaldı.

Kayıp referans

Referans ile kullanırsanız ( ref) o zaman myList've myListaynı olacaktır (sadece bir referans).

Not: Kullandığınız myList'parametreyi temsil etmek için kullanıyorum ChangeList(çünkü orijinalle aynı adı vermiştiniz)


20

İşte bunu anlamanın kolay bir yolu

  • Listeniz, yığın üzerinde oluşturulan bir nesnedir. Değişken myList, bu nesneye bir referanstır.

  • C # 'da nesneleri asla iletmezsiniz, referanslarını değere göre iletirsiniz.

  • Liste nesnesine içinde iletilen referans aracılığıyla eriştiğinizde ChangeList(örneğin sıralama sırasında) orijinal liste değiştirilir.

  • ChangeListYöntem üzerindeki atama , referansın değerine yapılır, bu nedenle orijinal listede hiçbir değişiklik yapılmaz (hala yığın üzerindedir, ancak artık yöntem değişkenine başvurulmamaktadır).


10

Bu bağlantı, C # 'ta referans olarak geçişi anlamanıza yardımcı olacaktır. Temel olarak, referans türündeki bir nesne bir yönteme değer olarak aktarıldığında, yalnızca o nesnede bulunan yöntemler nesnenin içeriğini değiştirebilir.

Örneğin List.sort () yöntemi Liste içeriğini değiştirir, ancak aynı değişkene başka bir nesne atarsanız, bu atama o yöntem için yereldir. Bu nedenle myList değişmeden kalır.

Ref anahtar sözcüğünü kullanarak referans türünde bir nesne geçirirsek, aynı değişkene başka bir nesne atayabiliriz ve bu, tüm nesnenin kendisini değiştirir.

(Düzenleme: Bu , yukarıda bağlantısı verilen belgelerin güncellenmiş sürümüdür.)


5

C #, söz konusu nesne çalıştırılmadığı sürece ICloneable(görünüşe göre Listsınıf yürütmez) değere göre geçtiğinde yüzeysel bir kopya yapar.

Bunun anlamı, Listkendisini kopyalamasıdır , ancak listedeki nesnelere yapılan referanslar aynı kalır; yani işaretçiler orijinal ile aynı nesnelere başvurmaya devam eder List.

Yeni Listreferanslarınızdaki şeylerin değerlerini değiştirirseniz, orijinali Listde değiştirirsiniz (çünkü aynı nesnelere referans veriyor). Bununla birlikte, daha sonra hangi myListreferansları tamamen yenisiyle değiştirirsiniz Listve şimdi yalnızca orijinal Listbu tam sayılara referansta bulunur.

Daha fazla bilgi için "Geçiş Parametreleri" hakkındaki bu MSDN makalesindeki Geçiş Başvuru Türü Parametreleri bölümünü okuyun .

StackOverflow'dan "C #'da Genel Bir Listeyi Nasıl Klonlarım " , bir Listenin nasıl derin bir kopyasının yapılacağı hakkında konuşuyor.


3

Yukarıda herkesin söylediklerine katılıyorum. Bu koda farklı bir yaklaşımım var. Temel olarak yeni listeyi global değil myList yerel değişkenine atıyorsunuz. ChangeList (List myList) imzasını private void ChangeList () olarak değiştirirseniz 3, 4 çıktısını görürsünüz.

İşte benim mantığım ... Liste referansla aktarılsa da, onu değere göre bir işaretçi değişkeni geçiriyormuş gibi düşünün ChangeList (myList) 'i çağırdığınızda, işaretçiyi (Global) myList'e geçiriyorsunuz. Şimdi bu (yerel) myList değişkeninde saklanır. Şimdi (yerel) myList ve (global) myList aynı listeyi gösteriyor. Şimdi bir sıralama yaparsınız => çalışır çünkü (yerel) myList, orijinal (global) myList'e başvurur. Sonra yeni bir liste oluşturur ve bu işaretçiyi (yerel) myList'e atarsınız. Ancak işlev çıkar çıkmaz (yerel) myList değişkeni yok edilir. HTH

class Test
{
    List<int> myList = new List<int>();
    public void TestMethod()
    {

        myList.Add(100);
        myList.Add(50);
        myList.Add(10);

        ChangeList();

        foreach (int i in myList)
        {
            Console.WriteLine(i);
        }
    }

    private void ChangeList()
    {
        myList.Sort();

        List<int> myList2 = new List<int>();
        myList2.Add(3);
        myList2.Add(4);

        myList = myList2;
    }
}

3

refAnahtar kelimeyi kullanın .

Kesin referans bak burada geçen parametreleri anlamak için.
Belirli, bakmak olmak bu kodun davranışlarını anlamak için.

DÜZENLEME: Sortaynı referans üzerinde çalışır (değer ile iletilir) ve dolayısıyla değerler sıralanır. Ancak, parametreye yeni bir örnek atamak işe yaramaz çünkü siz koymadıkça parametre değere göre iletilir ref.

Koyma ref, işaretçiyi vakanızdaki yeni bir örneğe referans olarak değiştirmenize izin verir List. Olmadan ref, mevcut parametre üzerinde çalışabilirsiniz, ancak başka bir şeye işaret edemezsiniz.


0

Referans tipinde bir nesne için ayrılmış iki bellek bölümü vardır. Biri yığın ve biri yığın halinde. Yığın içindeki parça (bir işaretçi olarak da bilinir), gerçek değerlerin depolandığı yığın içindeki parçaya referans içerir.

Ref anahtar sözcüğü kullanılmadığında, yalnızca yığındaki parçanın bir kopyası oluşturulur ve yönteme aktarılır - yığın içindeki aynı parçaya referans. Bu nedenle, yığın kısmında bir şeyi değiştirirseniz, bu değişiklik kalacaktır. Kopyalanan işaretçiyi değiştirirseniz - onu öbek içindeki başka bir yere başvurmak üzere atarsanız - bu, yöntemin dışındaki başlangıç ​​işaretçisini etkilemez.

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.