Ref ne zaman kullanılır ve C # 'da gerekli olmadığında


104

Programın hafızadaki durumu olan bir nesnem var ve ayrıca durumu değiştirmek için nesneyi ilettiğim başka çalışan işlevlerim var. Bunu işçi işlevlerine göre aktarıyorum. Ancak şu fonksiyonla karşılaştım.

byte[] received_s = new byte[2048];
IPEndPoint tmpIpEndPoint = new IPEndPoint(IPAddress.Any, UdpPort_msg);
EndPoint remoteEP = (tmpIpEndPoint);

int sz = soUdp_msg.ReceiveFrom(received_s, ref remoteEP); 

Kafamı karıştırıyor çünkü ikisi de received_sve remoteEPişlevden bir şeyler döndürüyor. Neden a'ya remoteEPihtiyaç duyuyor refve received_sgerekmiyor?

Ben de ac programcısıyım, bu yüzden işaretçileri kafamdan atmakta sorun yaşıyorum.

Düzenleme: Görünüşe göre C # 'daki nesneler kaputun altındaki nesneye işaret ediyor. Böylece, bir nesneyi bir işleve ilettiğinizde, nesne içeriğini işaretçi aracılığıyla değiştirebilirsiniz ve işleve iletilen tek şey nesnenin işaretçisidir, böylece nesnenin kendisi kopyalanmaz. Çift işaretçi gibi işlevde geçiş yapabilmek veya yeni bir nesne oluşturmak istiyorsanız ref veya out kullanırsınız.

Yanıtlar:


217

Kısa cevap: benim okuma argüman geçen makalesine .

Uzun cevap: bir referans türü parametresi değere göre iletildiğinde, nesnenin bir kopyası değil , yalnızca başvuru iletilir . Bu, C veya C ++ 'da bir işaretçi (değere göre) iletmek gibidir. Parametresi kendisinin değerine değişiklikler arayan tarafından görülen, ama referans noktaları nesne değişikliği olmaz olacak görülebilir.

Bir parametre (her türde) geçirildiğinde ile referans parametresi herhangi bir değişiklik arayan tarafından görülür o araçları - parametreye değişiklikler olan değişkene değişir.

Makale elbette tüm bunları daha ayrıntılı olarak açıklıyor :)

Faydalı cevap: neredeyse hiç ref / out kullanmanıza gerek kalmaz . Temelde başka bir dönüş değeri elde etmenin bir yoludur ve genellikle kesinlikle kaçınılmalıdır çünkü bu, yöntemin muhtemelen çok fazla şey yapmaya çalıştığı anlamına gelir. Durum her zaman böyle değildir ( TryParsevb. Makul kullanımın kanonik örnekleridir out) ancak ref / out kullanımı göreceli olarak nadir olmalıdır.


38
Kısa cevabınızı ve uzun cevabınızı karıştırdığınızı düşünüyorum; bu büyük bir makale!
Outlaw Programmer

23
@Outlaw: Evet, ancak kısa cevabın kendisi, makaleyi okuma yönergesi sadece 6 kelime uzunluğunda :)
Jon Skeet

27
Referans! :)
gonzobrains

5
Aslında potansiyel arayanları söylüyorsun çünkü @Liam ben değiştirebilir", size göre daha açık görebilirsiniz yaptığım gibi ref kullanarak, ama aslında (Anahtar kelime zaten ne yaptığını bilenler) diğer programcılar için kafa karıştırıcı olabilir değişken sen arama yönteminde kullanılır, yani onu farklı bir nesneye yeniden atayın (veya hatta mümkünse boşa atayın), bu yüzden ona takılıp kalmayın veya işim bittiğinde doğruladığınızdan emin olun ". Bu oldukça güçlüdür ve "bu nesne değiştirilebilir" den tamamen farklıdır, bu her zaman bir nesne başvurusunu parametre olarak ilettiğinizde böyle olur.
mbargiel

1
@ M.Mimpen: C # 7 (umarım) izin veririf (int.TryParse(text, out int value)) { ... use value here ... }
Jon Skeet

26

Ref olmayan bir parametreyi işaretçi ve ref parametresini çift işaretçi olarak düşünün. Bu bana en çok yardımcı oldu.

Değerleri neredeyse hiçbir zaman ref ile geçirmemelisiniz. Interop endişeleri olmasaydı, .Net ekibinin bunu asla orijinal spesifikasyona dahil etmeyeceğinden şüpheleniyorum. Ref parametrelerinin çözdüğü çoğu problemle başa çıkmanın OO yolu şudur:

Birden çok dönüş değeri için

  • Birden çok dönüş değerini temsil eden yapılar oluşturun

Yöntem çağrısının sonucu olarak bir yöntemde değişen ilkel öğeler için (yöntemin ilkel parametreler üzerinde yan etkileri vardır)

  • Yöntemi bir nesneye örnek yöntem olarak uygulayın ve yöntem çağrısının bir parçası olarak nesnenin durumunu (parametreleri değil) değiştirin
  • Çoklu dönüş değeri çözümünü kullanın ve dönüş değerlerini durumunuzla birleştirin
  • Bir yöntem tarafından işlenebilen durumu içeren bir nesne oluşturun ve bu nesneyi parametre olarak iletin, ilkellerin kendileri değil.

8
İyi tanrı. Bunu anlamak için bu 20x'i okumam gerekiyor. Bana basit bir şey yapmak için fazladan bir iş gibi geliyor.
PositiveGuy

.NET çerçevesi 1 numaralı kuralınıza uymuyor. ('Birden çok dönüş değeri için yapılar oluşturun'). Örneğin alın IPAddress.TryParse(string, out IPAddress).
Swen Kooij

@SwenKooij Haklısın. Parametreleri kullandıkları çoğu yerde ya (a) Bir Win32 API'sini sarıyorlar ya da (b) ilk günlerdeydi ve C ++ programcıları kararlar alıyorlardı.
Michael Meadows

@SwenKooij Bu cevabın yıllarca geciktiğini biliyorum, ama öyle. TryParse'a alışkınız, ancak bu iyi olduğu anlamına gelmez. Yerine eğer daha iyi olurdu if (int.TryParse("123", out var theInt) { /* use theInt */ }biz var candidate = int.TrialParse("123"); if (candidate.Parsed) { /* do something with candidate.Value */ }Daha kod, ama çok daha tutarlı C # dili tasarımı ile olduğunu.
Michael Meadows

9

Muhtemelen bütün bir C # uygulaması yazabilir ve hiçbir nesneyi / yapıyı ref ile iletemezsiniz.

Bana şunu söyleyen bir profesörüm vardı:

Referansları kullanacağınız tek yer şunlardan birini yaptığınız yerdir:

  1. Büyük bir nesneyi (yani nesnelerin / yapının içinde birden çok seviyeye nesneler / yapılar vardır) geçirmek istersiniz ve onu kopyalamak pahalı olur ve
  2. Bir Framework, Windows API veya bunu gerektiren başka bir API çağırıyorsunuz.

Bunu sadece yapabildiğin için yapma. Bir parametrede değerleri değiştirmeye başlarsanız ve dikkatinizi vermezseniz, bazı kötü böcekler tarafından ısırılabilirsiniz.

Tavsiyesine katılıyorum ve okuldan bu yana geçen beş artı yılımda, Framework veya Windows API'yi çağırmanın dışında buna hiç ihtiyacım olmadı.


3
"Takas" uygulamayı planlıyorsanız, ref ile geçmek faydalı olabilir.
Brian

@Chris, küçük nesneler için ref anahtar sözcüğünü kullanırsam sorun olur mu?
ManirajSS

@TechnikEmpire "Ancak, çağrılan işlevin kapsamındaki nesnede yapılan değişiklikler arayana geri yansıtılmaz." Bu yanlış. Bir Kişiyi'e iletirsem SetName(person, "John Doe"), name özelliği değişecek ve bu değişiklik arayana yansıtılacaktır.
M.Mimpen

@ M.Mimpen Yorumu silindi. O noktada C # 'ı zar zor alıyordum ve açıkça benim * $$ değerimi konuşuyordum. Dikkatimi çektiğiniz için teşekkürler.

@Chris - "Büyük" bir nesneyi geçmenin pahalı olmadığına eminim. Eğer onu değere göre geçirirseniz, hala tek bir işaretçiyi geçersiniz değil mi?
Ian

3

Alınan_s bir dizi olduğundan, o diziye bir gösterici geçiriyorsunuz. İşlev, temeldeki konumu veya işaretçiyi değiştirmeden, mevcut verileri yerinde işler. Ref anahtar sözcüğü, gerçek işaretçiyi konuma ilettiğinizi ve bu işaretçiyi dış işlevde güncellediğinizi belirtir, böylece dış işlevdeki değer değişir.

Örneğin bayt dizisi önceki ve sonraki aynı belleğe bir göstericidir, bellek henüz güncellenmiştir.

Uç nokta referansı aslında işaretleyiciyi dış işlevdeki Uç Noktaya, işlev içinde oluşturulan yeni bir örneğe güncelliyor.


3

Bir referansı, bir göstericiyi referans olarak geçirdiğiniz anlamına gelir. Bir ref kullanmamak, bir göstericiyi değere göre geçirdiğiniz anlamına gelir.

Daha da iyisi, az önce söylediklerimi görmezden gelin (muhtemelen yanıltıcıdır, özellikle değer türlerinde) ve Bu MSDN sayfasını okuyun .


Aslında doğru değil. En azından ikinci kısım. Referans kullansanız da kullanmasanız da, herhangi bir referans türü her zaman referans olarak aktarılacaktır.
Erik Funkenbusch

Aslında, biraz daha derinlemesine düşünürsek, bu doğru çıkmadı. Referans aslında ref türü olmadan değere göre aktarılır. Yani, referansın işaret ettiği değerleri değiştirmek orijinal verileri değiştirir, ancak referansın kendisini değiştirmek orijinal referansı değiştirmez.
Erik Funkenbusch

2
Referans olmayan bir referans türü referansla aktarılmaz. Referans türüne bir referans, değere göre iletilir. Ancak bir referansı, referansta bulunulan bir şeye bir işaretçi olarak düşünmek istiyorsanız, söylediklerim mantıklıdır (ancak bu şekilde düşünmek yanıltıcı olabilir). Bu yüzden uyarım.
Brian

Bağlantı öldü - bunun yerine bunu deneyin - docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
GIVE-ME-CHICKEN

0

Anladığım kadarıyla, Object sınıfından türetilen tüm nesneler işaretçi olarak aktarılırken sıradan türler (int, struct) işaretçi olarak aktarılmaz ve ref gerektirir. String hakkında emin değilim (sonuçta Object sınıfından mı türetildi?)


Bu biraz açıklama gerektirebilir. Değer ve referans tyoları arasındaki fark nedir? Cevap neden bir parametrede ref anahtar sözcüğünü kullanmakla ilgilidir?
oɔɯǝɹ

.Net'te işaretçiler, tür parametreleri ve arabirimler dışındaki her şey Object'ten türetilir. "Sıradan" türlerin de (doğru bir şekilde "değer türleri" olarak adlandırılır) nesneden miras aldığını anlamak önemlidir. Bundan sonra haklısınız: Değer türleri (varsayılan olarak) değere göre aktarılırken, başvuru türleri başvuruya göre iletilir. Bir değer türünü değiştirmek istediyseniz, yenisini döndürmek yerine (bunu bir yöntem içindeki bir referans türü gibi ele alın), üzerinde ref anahtar sözcüğünü kullanmanız gerekir. Ama bu kötü bir tarz ve bunu yapmak zorunda olduğunuzdan kesinlikle emin değilseniz bunu
yapmamalısınız

1
sorunuzu cevaplamak için: string nesneden türetilmiştir. bir değer türü gibi davranan bir referans türüdür (performans ve mantıksal nedenlerle)
buddybubble

0

Jon Skeet'in genel olarak cevabına ve diğer bazı cevaplara katılıyorum, ancak kullanmak için bir kullanım durumu var ref ve bu, performans optimizasyonlarını sıkılaştırmak için. Performans profili oluşturma sırasında, bir yöntemin geri dönüş değerini belirlemenin performans açısından küçük etkileri olduğu gözlemlenmiştir, oysa refdönüş değerinin bu parametreye doldurulduğu bir argüman olarak kullanılması , bu küçük darboğazın ortadan kaldırılmasına neden olur.

Bu gerçekten yalnızca optimizasyon çabalarının aşırı seviyelere götürüldüğü, okunabilirlikten ve belki de milisaniyelerden veya belki de milisaniyelerden tasarruf etmek için test edilebilirlik ve sürdürülebilirlikten ödün verildiği durumlarda yararlıdır.


-1

İlk olarak temel sıfır kuralı, ilgili TİPLER bağlamında, İlkel olmayanlar (Yığın) değeriyle (Yığın) ve İlkel Olmayan (Heap) ile aktarılır.

İlgili parametreler varsayılan olarak Değer tarafından aktarılır. Her şeyi ayrıntılı olarak açıklayan iyi bir gönderi. http://yoda.arachsys.com/csharp/parameters.html

Student myStudent = new Student {Name="A",RollNo=1};

ChangeName(myStudent);

static void ChangeName(Student s1)
{
  s1.Name = "Z"; // myStudent.Name will also change from A to Z
                // {AS s1 and myStudent both refers to same Heap(Memory)
                //Student being the non-Primitive type
}

ChangeNameVersion2(ref myStudent);
static void ChangeNameVersion2(ref Student s1)
{
  s1.Name = "Z"; // Not any difference {same as **ChangeName**}
}

static void ChangeNameVersion3(ref Student s1)
{
    s1 = new Student{Name="Champ"};

    // reference(myStudent) will also point toward this new Object having new memory
    // previous mystudent memory will be released as it is not pointed by any object
}

(Uyarı ile) diyebiliriz İlkel olmayan türler İşaretçilerden başka bir şey değildir Ve onları ref ile geçerken Double Pointer'ı geçiyoruz diyebiliriz


Tüm parametreler varsayılan olarak C # 'da değere göre geçirilir. Herhangi bir parametre referansla geçilebilir. Başvuru türleri için, (başvuruya göre veya değere göre) iletilen değerin kendisi bir başvurudur. Bu, nasıl geçtiğinden tamamen bağımsızdır.
2014

Kabul ediyorum @Servy! "Kullanılan" referans "veya" değer "kelimelerini duyduğumuzda, bir parametrenin bir referans mı yoksa değer parametresi mi olduğunu mu, yoksa ilgili türün bir referans mı yoksa değer türü 'olduğunu mu kastettiğimizi çok açık bir şekilde aklımızda tutmalıyız. Benim tarafımda kafa karışıklığı, önce Toprak sıfır kuralı dediğimde , İlkellerin değeri (yığın) ve İlkel Olmayan (Yığın) referansla geçirilir . Parametre değil, ilgili TİPLER'den kastım Parametreler hakkında konuştuğumuzda Doğrusunuz , Tüm parametreler varsayılan olarak C # ile değere göre aktarılır.
Surender Singh Malik

Bir parametrenin bir referans veya değer parametresi olduğunu söylemek standart bir terim değildir ve ne demek istediğiniz konusunda gerçekten belirsizdir. Parametrenin türü, bir başvuru türü veya bir değer türü olabilir. Herhangi bir parametre değere göre veya referans olarak geçilebilir. Bunlar, herhangi bir parametre için ortogonal kavramlardır. Gönderiniz bunu yanlış açıklıyor.
2014

1
Parametreleri referans veya değere göre iletirsiniz. "Başvuruya göre" ve "değere göre" terimleri, bir türün değer türü veya başvuru türü olup olmadığını açıklamak için kullanılmaz.
2014

1
Hayır, bu bir algılama meselesi değil . Bir şeye atıfta bulunmak için yanlış terimi kullandığınızda bu ifade yanlış olur . Kavramlara atıfta bulunmak için doğru ifadelerin doğru terminolojiyi kullanması önemlidir.
2014,
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.