'Ref' ve 'out' anahtar kelimeleri arasındaki fark nedir?


891

Ben bir işlevi tarafından değiştirilebilir böylece bir nesneyi geçmek gerekir bir işlev oluşturma. Arasındaki fark nedir:

public void myFunction(ref MyClass someClass)

ve

public void myFunction(out MyClass someClass)

Hangisini kullanmalıyım ve neden?


69
Siz: Değiştirilebilmesi için bir nesneyi geçirmemMyClass gerekiyor Bir classtür, yani bir başvuru türü gibi görünüyor . Bu durumda, ilettiğiniz nesne, / keyword myFunctionolmadan çift tarafından değiştirilebilir . aynı nesneyi işaret eden yeni bir başvuru alır ve aynı nesneyi istediği kadar değiştirebilir. Fark, anahtar olur, olacaktır alınan aynı aynı nesneye başvuru. Bu , referansı başka bir nesneye işaret edecek şekilde değiştirmek olsaydı önemli olurdu . refoutmyFunctionrefmyFunctionmyFunction
Jeppe Stig Nielsen

3
@ AnthonyKolesov'un oldukça mükemmel olduğu zaman burada kafa karıştırıcı cevapların miktarından şaşkınım.
o0 '.

Bir yöntemin bildirilmesi, bir yöntemin birden çok değer döndürmesini istediğinizde yararlıdır. Bir argüman null değerine atanabilir. Bu, yöntemlerin değerleri isteğe bağlı olarak döndürmesini sağlar.
Yevgraf Andreyevich Zhivago

Burada Örnek ile açıklanmıştır Daha anlaşılır :) dotnet-tricks.com/Tutorial/csharp/…
Prageeth godage

2
@ JeppeStigNielsen'in yorumu teknik olarak OP'nin asıl sorusuna (sadece) doğru cevaptır. Bir nesneyi , yöntemin nesneyi değiştirebilmesi için bir yönteme geçirmek için, (nesneye başvuru) nesnesini değere göre yönteme geçirin. Yöntem içindeki nesneyi nesne bağımsız değişkeni ile değiştirmek , yöntem kendi ayrı değişkenini içermesine rağmen (aynı nesneye gönderme yapan) orijinal nesneyi değiştirir .
David R Tribble

Yanıtlar:


1160

refderleyiciye işleve girmeden önce nesnenin başlatıldığını outsöylerken, derleyiciye nesnenin işlevin içinde başlatılacağını bildirir.

Öyleyse ref, iki yönlü olsa da, outsadece dışarıdadır.


270
Out'a özgü bir başka harika şey de fonksiyonun out parametresine ataması gerektiğidir. Atanmamış olarak bırakılmasına izin verilmiyor.
Daniel Earwicker

7
'ref' yalnızca değer türü için geçerlidir? Referans türü her zaman ref ile geçtiği için.
hatalı

3
Evet. Yapılar dahil değer türleri
Rune Grimstad

17
@faulty: Hayır, ref yalnızca değer türleri için geçerli değildir. ref / out, C / C ++ 'da işaretçiler gibidir, doğrudan nesne yerine nesnenin bellek konumu (dolaylı olarak C # ile) ilgilenir.
thr

52
@faulty: Referans belirteci kullanmazsanız, Referans türleri, C # 'da her zaman değere göre geçirilir. Myval = somenewval değerini ayarlarsanız, efekt yalnızca o işlev kapsamındadır. Ref anahtar kelimesi, myval değerini somenewval'a işaret edecek şekilde değiştirmenize izin verir.
JasonTrue

535

refModifiye araçlarının:

  1. Değer zaten ayarlanmış ve
  2. Yöntem okuyabilir ve değiştirebilir.

outModifiye araçlarının:

  1. Değer ayarlanmadı ve ayarlanana kadar yöntem tarafından okunamıyor .
  2. Yöntem , geri dönmeden önce ayarlamalıdır.

30
Bu yanıt, ref anahtar sözcüğünün aksine out anahtar sözcüğünü kullanırken derleyicinin uyguladığı kısıtlamaları en açık ve açık bir şekilde açıklar.
Dr.Wily's Apprentice

5
MSDN'den: Bir ref parametresinin kullanılmadan önce başlatılması gerekirken, bir out parametresinin geçirilmeden önce açıkça başlatılması gerekmez ve önceki herhangi bir değer göz ardı edilir.
Shiva Kumar

1
İle out, yöntem çağrılmadan önce başlatılmışsa, bu yöntemle ayarlanmadan önce yöntem içinde okunabilir mi? Yani, çağrılan yöntem argüman olarak çağırma yönteminin kendisine ilettiği şeyi okuyabilir mi?
Ocak

3
Panzercrisis, "out" için, çağrılan yöntem önceden ayarlanmışsa okuyabilir. ancak tekrar ayarlaması gerekir.
Robert Jebakumar2

146

Diyelim ki Dom, TPS raporlarıyla ilgili not hakkında Peter'ın kabininde görünüyor.

Dom bir ref argümanı olsaydı, notun basılı bir kopyası olurdu.

Dom dışlanmış bir argüman olsaydı, Peter'ın onunla birlikte alması için notun yeni bir kopyasını yazdırmasını isterdi.


54
ref Dom raporu Peter'da değiştirebilmesi için kurşun kalemle
yazardı

6
@Deebster biliyorsun, bu metafor sana hiç bir şey yapmadı, neden bu kadar işkence etmelisin? ;)
Michael Blackburn

21
eğlenceli ama eğitici, stackoverflow bunun gibi daha fazla
gönderiye

2
Birisinin bu cevabı sadece yarı komik bulması durumunda, lütfen "Ofis Alanı" filmini izleyin.
displayName

Dom ve Peters patronu, Dom'un behin Dom'a (argüman olarak) dayanacaktı, Peter çıktıyı Domd'a verene kadar her ikisini de yeniden yazdırmaya zorladı
Patrick Artner

57

Elimi bir açıklamada deneyeceğim:

Bence değer türlerinin nasıl çalıştığını anlıyoruz? Değer türleri (int, long, struct vb.) Şeklindedir. Bunları ref komutu olmadan bir işleve gönderdiğinizde verileri KOPYALAR . İşlevdeki bu verilere yaptığınız her şey orijinali değil yalnızca kopyayı etkiler. Ref komutu GERÇEK verileri gönderir ve yapılan değişiklikler işlevin dışındaki verileri etkiler.

Kafa karıştırıcı kısma, referans türlerine bakın:

Bir referans türü oluşturalım:

List<string> someobject = new List<string>()

Yeni bir nesne oluşturduğunuzda, iki bölüm oluşturulur:

  1. Bir nesneye ilişkin verileri tutan bellek bloğu .
  2. Bu veri bloğuna bir referans (işaretçi).

Şimdi bir yöntemi ref olmadan bir yönteme gönderdiğinizde, veriyi DEĞİL referans işaretçisini KOPYALAR . Şimdi buna sahipsiniz:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

Aynı nesneye işaret eden iki referans. Bir nesnedeki bir özelliği reference2 kullanarak değiştirirseniz, reference1 ile gösterilen aynı verileri etkiler.

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

Reference2'yi sıfırlar veya yeni verilere yönlendirirseniz, reference1'i veya veri referansı1'i etkilemez.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

Gönderdiğiniz Şimdi ne zaman ne olur SomeObject bir yönteme ref tarafından? Gerçek bir referans için SomeObject yöntemine gönderilir. Artık verilere tek bir referansınız var:

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

Peki bu ne anlama geliyor? İki ana şey dışında ref ile değil, bir gönderi göndermeyle tamamen aynı davranır:

1) Yöntemin içindeki başvuruyu iptal ettiğinizde, yöntemin dışındaki başvuruyu geçersiz kılar.

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2) Şimdi referansı tamamen farklı bir veri konumuna yönlendirebilirsiniz ve fonksiyonun dışındaki referans şimdi yeni veri konumuna işaret edecektir.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true

Sonuçta (ref durumunda) verilere sadece bir referans var, ancak iki takma ad var. Sağ?
Sadık

3
Açık açıklama için seçildi. Ama bunun arasındaki farkı açıklamak yok bu, soruya cevap vermez düşünmek refve outparametreleri.
Joyce Babu

1
İnanılmaz. outanahtar kelime ile aynı açıklayabilir misiniz ?
Asif Mushtaq

28

ref girip çıkıyor .

outGereksinimleriniz için yeterli olduğu yerlerde tercihli olarak kullanmalısınız .


tam olarak değil, kabul edilen cevap olarak yön ve yararsız ise değer türlerini göz ardı edilmezse ref.
kenny

@kenny: Biraz açıklığa kavuşturabilir misiniz lütfen - yani, cevabın ruhunu korumak ama algıladığınız yanlışlığı ortadan kaldırmak için hangi kelimeleri değiştirirdiniz? Cevabım bir acemi deli bir tahmin değil, ama yorumunuzdaki acele (terslik, yazım hataları) olduğunu varsayıyor gibi görünüyor. Amaç, farkları en az sayıda kelimeyle düşünmenin bir yolunu sağlamaktır.
Ruben Bartelink

(BTW Değer türlerine, referans türlerine, referansla geçen, değere göre geçen, COM ve C ++ ile açıklığa
kavuştuğunuzda

1
Nesne başvuruları değere göre iletilir ("ref" veya "out" anahtar sözcüğü kullanılması hariç). Nesneleri kimlik numaraları olarak düşünün. Bir sınıf değişkeni "Object # 1943" 'i tutarsa ​​ve bu değişkeni değere göre bir rutine geçirirse, bu rutin Object # 1943 üzerinde değişiklik yapabilir, ancak değişkenin "Object # 1943" dışındaki herhangi bir şeye işaret edemez. Değişken referans olarak iletildiyse, rutin değişken noktasını "Object # 5441" tutabilir.
Supercat

1
@supercat: ref vs val (ve bu takip anaolojisi) açıklamanızı seviyorum. Kenny'nin aslında ona açıkladığı hiçbir şeye ihtiyacı olmadığını düşünüyorum, (nispeten) yorumları olduğu gibi kafa karıştırıcı. Keşke herkes bu kafa karışıklığı yorumlarını kaldırabilseydik, sadece herkesi karıştırıyorlar. Tüm bu saçmalıkların temel nedeni, kenny'nin cevabımı yanlış okuduğu ve henüz eklenmesi / kaldırılması / değiştirilmesi gereken tek bir kelimeye işaret etmediği anlaşılıyor. Üçümüzden hiçbiri, daha önce bilmediğimiz tartışmadan bir şey öğrenmedik ve diğer cevabın gülünç sayıda upvotes var.
Ruben Bartelink

18

dışarı:

C # 'da bir yöntem yalnızca bir değer döndürebilir. Birden fazla değer döndürmek isterseniz, out anahtar sözcüğünü kullanabilirsiniz. Çıkış değiştiricisi referans dönüş olarak geri döner. En basit cevap, “out” anahtar kelimesinin yöntemden değer almak için kullanılmasıdır.

  1. Arama fonksiyonundaki değeri başlatmanız gerekmez.
  2. Aranan işlevde değeri atamanız gerekir, aksi takdirde derleyici bir hata bildirir.

ref:

C # 'da, method parametresine bağımsız değişken olarak int, float, double vb. Gibi bir değer türü ilettiğinizde, değere göre iletilir. Bu nedenle, parametre değerini değiştirirseniz, yöntem çağrısındaki bağımsız değişkeni etkilemez. Ancak parametreyi “ref” anahtar kelimesi ile işaretlerseniz, gerçek değişkene yansır.

  1. İşlevi çağırmadan önce değişkeni başlatmanız gerekir.
  2. Yöntemdeki ref parametresine herhangi bir değer atamak zorunlu değildir. Değeri değiştirmezseniz, değeri “ref” olarak işaretlemeniz gerekir?

"C # 'ta bir yöntem yalnızca bir değer döndürebilir. Birden fazla değer döndürmek isterseniz, out anahtar sözcüğünü kullanabilirsiniz." Ayrıca değer döndürmek için "ref" kullanabilirsiniz. Bir yöntemden birden fazla değer döndürmek istiyorsak ref ve out'ı kullanabilir miyiz?
Ned

1
C # 7'de ValueTuples ile birden çok değer döndürebilirsiniz.
Iman Bahrampour

13

Köpeği Uzatmak, Kedi örneği. Ref ile ikinci yöntem, arayan tarafından başvurulan nesneyi değiştirir. Dolayısıyla "Kedi" !!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

8

Bir başvuru türünden (sınıf) geçtiğiniz için, kullanıma gerek yoktur, refçünkü varsayılan olarak yalnızca gerçek nesneye bir referans iletilir ve bu nedenle referansın arkasındaki nesneyi her zaman değiştirirsiniz.

Misal:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

Bir sınıfı geçtiğiniz sürece ref, yönteminizin içindeki nesneyi değiştirmek istiyorsanız kullanmanız gerekmez .


5
Bu yalnızca yeni bir nesne oluşturulup döndürülmediğinde çalışır. Yeni bir nesne oluşturulduğunda, eski nesneye yapılan başvuru kaybolur.
etsuba

8
Bu yanlış - aşağıdakileri deneyin: sonuna kadar yürütmek someObject = nulliçin ekleyin Bar. Yalnızca Barörneğe başvuru null olduğundan kodunuz iyi çalışır . Şimdi değiştirmek Bariçin Bar(ref MyClass someObject)tekrar çalıştırmak - Bir alırsınız NullReferenceExceptionçünkü Fooörneğine bireyin referans çok nulled edilmiştir.
Keith

8

refve outaşağıdaki farklılıklar dışında benzer şekilde davranırlar.

  • refdeğişken kullanılmadan önce başlatılmalıdır. outdeğişken atamadan kullanılabilir
  • outparametresi, onu kullanan işlev tarafından atanmamış bir değer olarak değerlendirilmelidir. Bu nedenle, outçağrı kodunda başlatılmış parametre kullanabiliriz , ancak işlev yürütüldüğünde değer kaybolacaktır.


6

"Baker"

Bunun nedeni, ilkinin dizi referansınızı "Baker" u işaret edecek şekilde değiştirmesidir. Referansı değiştirmek mümkündür çünkü ref anahtar sözcüğünden geçtiniz (=> bir dizeye yapılan referansa bir referans). İkinci çağrı dizeye yapılan başvurunun bir kopyasını alır.

string ilk başta bir tür özel görünüyor. Ancak dize yalnızca bir referans sınıfıdır ve

string s = "Able";

sonra s, "Able" metnini içeren bir dize sınıfına başvurudur! Aynı değişkene üzerinden başka bir atama

s = "Baker";

orijinal dizeyi değiştirmez, sadece yeni bir örnek oluşturur ve bu örneği gösterelim!

Aşağıdaki küçük kod örneğiyle deneyebilirsiniz:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

Ne bekliyorsunuz? Alacağınız şey hala "Able" çünkü s2 referansını başka bir örneğe ayarlarken, s2 orijinal örneği gösterir.

EDIT: string de değişmezdir, yani mevcut bir dize örneğini değiştiren hiçbir yöntem veya özellik yoktur (dokümanlar içinde bir tane bulmayı deneyebilirsiniz, ancak herhangi bir yüzgeç alamayacaksınız). Tüm dize düzenleme yöntemleri yeni bir dize örneği döndürür! (Bu nedenle StringBuilder sınıfını kullanırken genellikle daha iyi bir performans elde edersiniz)


1
Kesinlikle. Bu nedenle, "Bir referans türünü (bir sınıfı) geçtiğiniz için ref kullanmaya gerek yoktur" demek kesinlikle doğru değildir.
Paul Mitchell

Teorik olarak söylemek doğru çünkü dizelerde mümkün olmayan "değiştirilebilecek şekilde" yazdı. Ancak değişmez nesneler nedeniyle "ref" ve "out" referans türleri için de çok yararlıdır! (.Net çok fazla değişmez sınıf içeriyor!)
mmmmmmmm

Evet haklısın. Çoğu nesne değiştirilebilir olduğu için dizeler gibi değişmez nesneleri düşünmedim.
Albic

1
Bu, emin olmak için LQP'de görmek için şaşırtıcı bir cevaptır; başka bir yoruma uzun ve kapsamlı bir yanıt gibi görünmesi dışında yanlış bir şey yok (orijinal soru Able ve Baker'dan revizyonlarının hiçbirinde bahsetmediğinden), sanki bu bir forum gibiydi. Sanırım bu gerçekten ne zaman geri dizildi değildi.
Nathan Tuggy

6

ref , ref parametresindeki değerin önceden ayarlanmış olduğu anlamına gelir; yöntem bunu okuyabilir ve değiştirebilir. Ref anahtar sözcüğünü kullanmak, arayanın parametrenin değerini başlatmaktan sorumlu olduğunu söylemekle aynıdır.


out derleyiciye nesnenin başlatılmasının işlevin sorumluluğu olduğunu, işlevin out parametresine ataması gerektiğini söyler. Atanmamış olarak bırakılmasına izin verilmiyor.


5

Dışarı: Bir return deyimi, bir işlevden yalnızca bir değer döndürmek için kullanılabilir. Ancak, çıktı parametrelerini kullanarak bir işlevden iki değer döndürebilirsiniz. Çıktı parametreleri referans parametreler gibidir, ancak verileri yöntemden ziyade yöntemin dışına aktarırlar.

Aşağıdaki örnek bunu göstermektedir:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

ref: Bir referans parametresi, bir değişkenin hafıza konumuna bir referanstır. Parametreleri referans olarak ilettiğinizde, değer parametrelerinden farklı olarak, bu parametreler için yeni bir depolama yeri oluşturulmaz. Referans parametreleri, yönteme sağlanan gerçek parametrelerle aynı bellek konumunu temsil eder.

C # 'da, ref anahtar sözcüğünü kullanarak referans parametrelerini bildirirsiniz. Aşağıdaki örnek bunu göstermektedir:

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}

4

ref ve out referansları ve C ++ gibi işaretçiler tarafından geçirilmesi gibi çalışır.

Ref için argüman bildirilmeli ve başlatılmalıdır.

Dışarıda, argüman beyan edilmelidir, ancak başlatılabilir veya başlatılamayabilir

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);

1
Bir değişken satır içi bildirebilirsiniz: out double Half_nbr.
Sebastian Hofmann

4

Yazma Zamanı:

(1) Arama yöntemini yaratıyoruz Main()

(2) bir List nesnesi oluşturur (bu bir referans türü nesnesidir) ve bunu değişkende saklar myList.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

Çalışma Zamanı Sırasında:

(3) Çalışma zamanı, bir adresi saklamak için yeterince geniş olan # 00'da yığın üzerine bir bellek ayırır (# 00 = myList, çünkü değişken adları gerçekten bellek konumları için takma adlardır)

(4) Çalışma zamanı bellekte #FF yığınında bir liste nesnesi oluşturur (tüm bu adresler örneğin sakes'dir)

(5) Çalışma zamanı daha sonra nesnenin başlangıç ​​adresini #FF # 00'da depolar (veya kelimelerde List nesnesinin başvurusunu işaretçide depolar myList)

Yazma Zamanına Dön:

(6) Daha sonra List nesnesini myParamListçağrılan yönteme argüman olarak iletiriz ve buna modifyMyListyeni bir List nesnesi atarız

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

Çalışma Zamanı Sırasında:

(7) Çalışma zamanı, çağrılan yöntem için çağrı yordamını başlatır ve bunun bir parçası olarak, parametre türlerini kontrol eder.

(8) Referans türünü bulduktan sonra, parametre değişkenini yumuşatmak için # 04'deki yığın üzerine bir bellek ayırır myParamList.

(9) Daha sonra #FF değerini de içinde saklar.

(10) Çalışma zamanı, bellek konumu # 004'teki öbek üzerinde bir liste nesnesi oluşturur ve # 04'teki #FF değerini bu değerle değiştirir (veya orijinal List nesnesinin kaydının silinmesi ve bu yöntemdeki yeni List nesnesinin işaret edilmesi)

# 00'daki adres değiştirilmez ve #FF referansını korur (veya orijinal myListişaretçi rahatsız edilmez).


Ref anahtar usul parametreleri için bir yığın ayırma olacağı anlamına gelir (8) ve (9) için çalıştırıcı kod üretimi atlamak için bir derleyici yönergesidir. Nesne üzerinde #FF üzerinde çalışmak için orijinal # 00 işaretçisini kullanacaktır. Orijinal işaretçi başlatılmazsa, çalışma zamanı değişkenin başlatılmadığı için devam edemeyeceğinden şikayet eder

Dışarı anahtar kelime hemen hemen (9) ve (10) 'de ufak bir değişiklikle ref aynı olan bir derleyici direktifi olduğunu. Derleyici, bağımsız değişkenin başlatılmasını bekler ve yığın üzerinde bir nesne oluşturmak ve başlangıç ​​adresini bağımsız değişken değişkeninde saklamak için (8), (4) ve (5) ile devam eder. Başlatılmamış bir hata atılmayacak ve kaydedilmiş önceki referanslar kaybolacaktır.


3

Hem de bir sınıfın farklı bir örneğine yeniden atamakta başkasının değişkene sizi izin verilmesi gibi, vb birden çok değer döndürmek kullanarak refveya outbaşkasının size sağladıkları değişken ile ne yapmak niyetinde onlardan ihtiyaç ve biliyorum sağlar

  • Sen gerekmez ref ya outsen yapacaksın bütün modify şeyler ise MyClass argüman geçirilir örneği someClass.

    • Çağıran yöntem gibi değişiklikleri görecek someClass.Message = "Hello World"kullanmak ister ref, outya da hiçbir şey
    • someClass = new MyClass()İçine yazmak yalnızca yöntem kapsamında myFunction(someClass)görülen nesneyi değiştirir . Çağıran yöntem, oluşturduğu ve yönteminize ilettiği orijinal örneği hâlâ biliyorsomeClassmyFunctionMyClass
  • Sen gerekmez ref veya outTakaslamadan düşünüyorsanız someClassçağıran yöntemini yepyeni nesne için out ve istediğiniz değişimi görmek için

    • someClass = new MyClass()İçine yazmak myFunction(out someClass), çağrılan yöntemle görülen nesneyi değiştirirmyFunction

Başka programcılar da var

Ve verileriyle ne yapacağınızı bilmek istiyorlar. Milyonlarca geliştirici tarafından kullanılacak bir kütüphane yazdığınızı düşünün. Yöntemlerinizi çağırdıklarında değişkenleriyle ne yapacağınızı bilmelerini istersiniz.

  • Kullanma ref, "Metodumu çağırdığınızda bir değere atanan bir değişkeni ilet." işim bittiğinde "

  • Kullanmak out, "Benim yöntemime bir yer tutucu değişkeni ilet. Bir değere sahip olup olmadığı önemli değil; derleyici beni yeni bir değere atamaya zorlayacak. Kesinlikle nesnenin sizin belirttiğiniz nesnenin benim yöntem çağırmadan önce değişken, yaptığım zaman farklı olacak

Bu arada, C # 7.2'de inde bir değiştirici var

Bu da yöntemin farklı bir örnek için geçirilen örneği değiştirmesini önler. Bunu milyonlarca geliştiriciye "orijinal değişken referansınızı iletin ve dikkatlice hazırlanmış verilerinizi başka bir şey için değiştirmeyeceğime söz veriyorum" gibi düşünün. inbazı özellikleri vardır ve bazı durumlarda kısa bir in intderleyici ile uyumlu hale getirmek için örtülü bir dönüşüm gerekli olabilir gibi geçici bir int yapacak, kısa kısa genişletmek, referans ile geçmek ve bitirmek. Bunu yapabilir, çünkü onunla uğraşmayacağınızı beyan ettiniz.


Microsoft bunu .TryParsesayısal türlerdeki yöntemlerle yaptı:

int i = 98234957;
bool success = int.TryParse("123", out i);

Parametreyi outburada aktif bir şekilde beyan ettikleri gibi işaretleyerek " kesinlikle 98234957'nin özenle hazırlanmış değerini başka bir şey için değiştireceğiz"

Tabii ki, değer türlerini ayrıştırma gibi şeyler için yapmak zorundalar çünkü ayrıştırma yönteminin başka bir şey için değer türünü değiştirmesine izin verilmezse çok iyi çalışmaz .. Ama bazılarında hayali bir yöntem olduğunu hayal edin oluşturduğunuz kütüphane:

public void PoorlyNamedMethod(out SomeClass x)

Bunun bir outolduğunu görebiliyorsunuz ve böylece saatlerde sayıları kırarak, mükemmel SomeClass'ı yaratırsanız:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

Bu mükemmel bir sınıf yapmak için tüm saatler harcanan zaman kaybıydı. Kesinlikle atılacak ve yerine PoorlyNamedMethod geçecek


3

Kısa cevap arayanlar için.

Hem refve outanahtar kelimeler geçmesine-by için kullanılır reference.


Bir refanahtar kelime değişkeni bir değere sahip olmalı veya bir nesneye veya null geçmeden önce başvurmalıdır .


Bunun aksine ref, bir outanahtar kelime değişkeni bir değere sahip olmalı ya da bir nesneye ya da geçtikten null sonra , ayrıca bir değere ya da geçmeden önce bir nesneye başvurmaya gerek duymamalıdır.


2

Birçok mükemmel açıklamayı göstermek için aşağıdaki konsol uygulamasını geliştirdim:

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
  • AppendWorld: StringListAdlı bir kopyası LiStriiletilir. Yöntemin başlangıcında, bu kopya orijinal listeye başvurur ve bu nedenle bu listeyi değiştirmek için kullanılabilir. Daha sonra yöntemin içinde, orijinal listeyi etkilemeyen LiStribaşka bir List<string>nesneye başvurur.

  • HalloWelt: LiStriRefzaten başlatılmış olanın takma adıdır ListStringRef. Geçirilen List<string>nesne yenisini başlatmak için kullanılır, bu nedenle refgerekliydi.

  • CiaoMondo: LiStriOutbir takma adıdır ListStringOutve başlatılması gerekir.

Bu nedenle, bir yöntem iletilen değişken tarafından başvurulan nesneyi değiştirirse, derleyici kullanmanıza izin vermez ve derleyiciyi değil kodun okuyucusunu karıştırdığı için outkullanmamalısınız ref. Yöntem iletilen bağımsız değişkeni başka bir nesneye başvuru yapacaksa, refzaten başlatılmış bir nesne ve outiletilen bağımsız değişken için yeni bir nesne başlatması gereken yöntemler için kullanın. Bunun yanı sıra, refve outaynı davranırlar.


1

Hemen hemen aynılar - tek fark, bir çıkış parametresi olarak ilettiğiniz bir değişkenin başlatılması gerekmiyor ve ref parametresini kullanan yöntemin bir şeye ayarlaması gerekiyor.

int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

Ref parametreleri, değiştirilebilecek veriler için, dışarı parametreleri, işlev için zaten bir şey için döndürme değerini kullanan ek bir çıktı (örneğin int.TryParse) olan veriler içindir.


1

Ben ikisini de kullanarak bir örnek göstermiştir Aşağıda Ref ve out . Şimdi, hepiniz ref ve out hakkında temizlenmiş olacaksınız.

Aşağıdaki örnekte yorum yaptığımda // myRefObj = yeni myClass {Name = "ref dış çağrılan !!"}; çizgi, belirten bir hata alırsınız "atanmamış yerel değişken 'myRefObj' Kullanımı" , ancak böyle bir hata içinde olduğu out .

Nerede kullanılır? Ref : in parametresiyle bir prosedür çağırdığımızda ve aynı parametre o proc'un çıkışını saklamak için kullanılacaktır.

Nerede Kullanılır: parametresinde no olmadan bir prosedür çağırdığımızda ve aynı param bu değeri proc'dan döndürmek için kullanılacaktır. Çıktıya da dikkat edin

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 

1
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

"ref" kullandığınızda size tam farkını açıklayacak olan bu kodu kontrol edebilirsiniz.

ancak "out" komutunu kullandığınızda, int / string'i başlatırken veya başlatmazsa her iki koşulda da çalışır ancak u, bu işlevdeki int / string'i başlatmalıdır


1

Ref: ref anahtar sözcüğü, bağımsız değişkeni başvuru olarak iletmek için kullanılır. Bu, bu parametrenin değeri yöntemde değiştirildiğinde, çağıran yönteme yansıtıldığı anlamına gelir. Ref anahtar sözcüğü kullanılarak iletilen bir bağımsız değişken, çağrılan yönteme geçirilmeden önce çağrı yönteminde başlatılmalıdır.

Out: out anahtar sözcüğü, ref anahtar sözcüğü gibi bir bağımsız değişkeni iletmek için de kullanılır, ancak bağımsız değişken buna herhangi bir değer atanmadan iletilebilir. Out anahtar sözcüğü kullanılarak iletilen bir bağımsız değişken, çağrılan yönteme geri dönmeden önce çağrılan yöntemde başlatılmalıdır.

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

Yöntem aşırı yüklemesinde ref ve out

Hem ref hem de out, aynı anda aşırı yöntem yüklemesinde kullanılamaz. Bununla birlikte, ref ve out çalışma zamanında farklı şekilde ele alınır, ancak derleme zamanında aynı şekilde tedavi edilirler (CLR ref ve out için IL oluştururken ikisi arasında ayrım yapmaz).


0

Bir parametre, arasındaki fark alan bir yöntemin açısından refve outbu C # olduğunu yöntemler her yazmam gereken gerektirir outdönmeden önce parametresi ve bir olarak geçen daha böyle bir parametreyle başka bir şey yapmamalısınız outparametre veya yazmadan , outbaşka bir yönteme parametre olarak aktarılana veya doğrudan yazılana kadar. Bazı diğer dillerin bu tür gereksinimleri getirmediğini unutmayın; C # 'da bir outparametre ile bildirilen bir sanal veya arabirim yöntemi , bu parametreler üzerinde herhangi bir özel kısıtlama getirmeyen başka bir dilde geçersiz kılınabilir.

Arayanın bakış açısından, C # birçok durumda bir outparametreyle bir yöntem çağrıldığında geçirilen değişkenin önce okunmadan yazılmasına neden olacağını varsayar . Diğer dillerde yazılmış yöntemler çağrıldığında bu varsayım doğru olmayabilir. Örneğin:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

Eğer myDictionarytanımlar, bir IDictionary<TKey,TValue>C # dışında bir dilde yazılmış uygulama, olsa bile MyStruct s = new MyStruct(myDictionary);bir atama gibi görünüyor, potansiyel bırakabilirim sdeğiştirilmemiş.

VB.NET'te yazılan kurucuların, C # 'dan farklı olarak, çağrılan yöntemlerin herhangi bir outparametreyi değiştirip değiştirmeyeceği ve tüm alanları koşulsuz olarak temizleyip temizlemeyeceği konusunda herhangi bir varsayımda bulunmadığını unutmayın . Yukarıda belirtilen garip davranış, tamamen VB veya tamamen C # ile yazılmış kod ile oluşmaz, ancak C # ile yazılmış kod VB.NET'te yazılmış bir yöntemi çağırdığında ortaya çıkabilir.


0

Eğer parametrenizi bir ref olarak iletmek istiyorsanız, parametre derleyicinin kendisine geçmeden önce parametreyi başlatmalısınız. Derleyici hata gösterecektir.Ama out parametresi durumunda, nesne parametresini Nesneyi çağıran yöntemin kendisinde başlatabilirsiniz.


-3

İşlevin içinden geçirilen referans parametresinin doğrudan üzerinde çalışıldığını unutmayın.

Örneğin,

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

Bu Köpek yazacak, Cat değil. Bu nedenle doğrudan someObject üzerinde çalışmalısınız.


6
Buradaki her şey hemen hemen doğru olsa da, referans ile veya dışarıdan değere göre farkı gerçekten açıklamaz. En iyi ihtimalle referans ve değer / değişmez tipler arasındaki farkı açıklar.
Conrad Frix

Bu kodun kedi yazmasını istiyorsanız, lütfen bu nesneyi 'ref' tuşuyla birlikte şu şekilde geçirin: public static void Bar (ref MyClass someObject), Bar (ref myObject);
Daniel Botero Correa

-4

Ben bu kadar iyi olmayabilir, ama kesinlikle dizeleri (onlar teknik olarak referans türleri ve yığın üzerinde canlı olmasına rağmen) referans değil, değer tarafından geçirilir?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

Bu nedenle, değişiklikleri yapan işlevin kapsamı dışında değişiklik olmasını istiyorsanız ref'ye ihtiyacınız vardır, aksi takdirde bir referans geçmezsiniz.

Bildiğim kadarıyla, sadece yapılar / değer türleri ve dizenin kendisi için ref'ye ihtiyacınız vardır, çünkü dize, bir değer türü gibi davranan ancak bir değer türü değildir.

Burada tamamen yanlış olabilirim, yeniyim.


5
Stack Overflow'a hoş geldiniz, Edwin. Dizeler, bildiğim kadarıyla, diğer herhangi bir nesne gibi, referans olarak geçirilir. Dizeler değişmez nesneler olduğu için kafanız karışabilir, bu nedenle referansla geçildikleri açık değildir. Dizenin Capitalize(), dizenin içeriğini büyük harflerle değiştirecek bir yöntemi olduğunu düşünün . Eğer daha sonra çizgi değiştirdiyseniz a = "testing";ile a.Capitalize();, sonra çıktı "MERHABA" değil "Merhaba" olurdu. Değişmez tiplerin avantajlarından biri, referansları geçebilmeniz ve değeri değiştiren diğer kodlar hakkında endişelenmemenizdir.
Don Kirkby

2
Bir tipin ortaya koyabileceği üç temel anlambilim türü vardır: değişebilir referans anlambilimi, değişken değer anlambilimi ve değişmez anlambilim. Alan veya özelliği m olan T türünün x ve y değişkenlerini göz önünde bulundurun ve x'in y'ye kopyalandığını varsayın. T'nin referans semantiği varsa, xm'deki değişiklikler ym tarafından gözlemlenir. T değer semantiği varsa, ym'yi etkilemeden xm değişebilir. T'nin değişmez semantiği varsa, ne xm ne de ym değişmez. Değişmez anlambilim, referans veya değer nesneleri ile simüle edilebilir. Dizeler değişmez referans nesneleridir.
Supercat
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.