C # neden referansların geri dönüşünü desteklemiyor?


141

.NET'in başvuruların geri dönüşünü desteklediğini okudum, ancak C # desteklemiyor. Özel bir nedeni var mı? Neden böyle bir şey yapamam:

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

8
hmm ... alıntı lütfen?
RPM1984

5
C # değer ve referans türlerine sahiptir ve her ikisi de ne demek istediğinizi döndürebilir.
yeniden çalıştır

Ben böyle bir şey bahsediyorumreturn ref x
Tom Sarduy

2
4 yıl sonra, tam olarak bunu C# 7:) ile yapabilirsiniz
Arghya C

Yanıtlar:


188

Bu soru 23 Haziran 2011 tarihinde blogumun konusuydu . Bu mükemmel soru için teşekkürler!

C # ekibi C # 7 için bunu düşünüyor. Ayrıntılar için bkz. Https://github.com/dotnet/roslyn/issues/5233 .

GÜNCELLEME: Bu özellik onu C # 7'ye getirdi!


Haklısın; .NET, yönetilen başvuruları değişkenlere döndüren yöntemleri destekler . .NET , diğer değişkenlere yönetilen başvurular içeren yerel değişkenleri de destekler . (Ancak , çöp toplama öyküsünü aşırı derecede karmaşıklaştırdığından, .NET'in diğer değişkenlere yönetilen başvurular içeren alanları veya dizileri desteklemediğini unutmayın . Ayrıca "değişkene yönetilen başvuru" türleri nesneye dönüştürülemez ve bu nedenle genel türlere veya yöntemlere argümanlar yazın.)

Bazı nedenlerden dolayı yorumcu "RPM1984" bu konuda bir alıntı istedi. RPM1984 .NET'in bu özelliği hakkında bilgi için CLI belirtimi Bölüm I Kısım 8.2.1.1, "Yönetilen işaretçiler ve ilgili türler" i okumanızı öneririz.

Her iki özelliği de destekleyen bir C # sürümü oluşturmak tamamen mümkündür. O zaman böyle şeyler yapabilirsin

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

ve sonra

int a = 123;
int b = 456; 
ref int c = ref Max(ref a, ref b); 
c += 100;
Console.WriteLine(b); // 556!

Ben bu özellikleri destekleyen C # bir sürümünü inşa etmenin mümkün olduğunu ampirik olarak biliyoruz ben çok yapmış çünkü . Gelişmiş programcılar, özellikle yönetilmeyen C ++ kodunu taşıyan kişiler, genellikle bize daha fazla C ++ benzeri şeyler ister, aslında işaretçileri kullanmanın ve hafızayı her yere sabitlemenin büyük çekiçinden kurtulmak zorunda kalmadan referanslarla bir şeyler yapma yeteneği ister. Yönetilen referansları kullanarak, çöp toplama performansınızı azaltmanın maliyetini ödemeden bu avantajları elde edersiniz.

Bu özelliği dikkate aldık ve aslında geri bildirimlerini almak için diğer dahili ekiplere gösterilecek kadar uyguladık. Ancak şu anda araştırmamıza dayanarak , özelliğin gerçek bir desteklenen dil özelliğine dönüştürmek için yeterince geniş bir cazibe veya zorlayıcı kullanım örneği olmadığına inanıyoruz . Daha yüksek önceliklerimiz ve sınırlı miktarda zaman ve çaba harcıyoruz, bu nedenle bu özelliği yakın zamanda yapmayacağız.

Ayrıca, düzgün bir şekilde yapılması için CLR'de bazı değişiklikler yapılması gerekir. Şu anda CLR, geri dönüş yöntemlerini yasal ancak doğrulanamaz olarak değerlendiriyor çünkü bu durumu tespit eden bir dedektörümüz yok:

ref int M1(ref int x)
{
    return ref x;
}

ref int M2()
{
    int y = 123;
    return ref M1(ref y); // Trouble!
}

int M3()
{
    ref int z = ref M2();
    return z;
}

M3, M2'nin yerel değişkeninin içeriğini döndürür, ancak bu değişkenin ömrü sona erdi! Açık bir şekilde yok ref-döner kullanımlarını saptayan bir dedektör yazmak mümkündür olmayan yığın güvenlik ihlal eder. Yapacağımız şey böyle bir dedektör yazmak ve eğer dedektör yığın güvenliğini kanıtlayamazsa, programın o bölümünde ref iadelerinin kullanılmasına izin vermezdik. Bunu yapmak çok büyük bir geliştirme işi değil, ancak tüm vakaları gerçekten aldığımızdan emin olmak için test ekipleri üzerinde çok fazla yük var. Bu, özelliğin maliyetini şu anda faydaların maliyetlerden ağır basmadığı noktaya kadar artıran başka bir şey.

Bana bu özelliği neden istediğini açıklayabilirsen, bunu gerçekten takdir ediyorum . Gerçek müşterilerden neden istedikleri hakkında ne kadar fazla bilgiye sahipsek, bir gün ürüne girme olasılığı o kadar yüksek olur. Bu sevimli küçük bir özellik ve yeterli ilgi varsa bir şekilde müşterilere almak istiyorum.

(Ayrıca ilgili sorulara bakın o C # Değişken bir başvuru Dönüş Mümkün mü? Ve ++ ben C gibi bir C # işlevi içinde bir başvuru kullanabilir miyim? )


4
@EricLippert: İkna edici bir örneğim yok, sadece merak ettiğim bir şey. Mükemmel ve ilgi çekici yanıt
Tom Sarduy

1
@Eric: Örneğinizde, döndükten sonra y hayatta kalmak için daha uygun olmaz mıydı M2? Bu özelliği yerliler yakalamak lambdas gibi çalışmasını beklenir. Yoksa teklif ettiğiniz davranış CLR bu senaryoyu nasıl işlediğinden mi?
Fede

3
@Eric: IMHO, değer türlerine özellikler döndüren referanslara sahip olma yeteneği, .net dillerinde büyük bir eksikliktir. Eğer Arr bir değer tipinde bir Array ise (örneğin Point), örneğin Arr (3) .X = 9 diyebilir ve Arr (9) .X ve hatta SomeOtherArray (2) değerini değiştirmediğini bilir. X; Arr bunun yerine bazı referans türlerinden oluşan bir dizi olsaydı, böyle bir garanti mevcut olmazdı. Bir dizinin dizin oluşturma işlecinin bir başvuru döndürmesi son derece yararlıdır; Başka hiçbir koleksiyonun böyle bir işlevsellik sağlayamaması oldukça talihsiz bir durum.
supercat

4
Eric, görülme olasılığını en üst düzeye çıkarmak için (son paragrafta önerdiğiniz gibi) senaryolarla ilgili geri bildirim sağlamanın en iyi yolu nedir? MS Connect? UserVoice (keyfi 10 yazı / oylama sınırı ile)? Başka bir şey?
Roman Starkov

1
@ThunderGr: Bu "güvensiz" için C # felsefesidir - muhtemelen bellekte güvensiz olan bir kod yazarsanız, C # "güvensiz" olarak işaretlemeniz konusunda ısrar eder, böylece bellek güvenliği için sorumluluk alırsınız. C #, söz konusu değişkenin yönetilmeyen tipte olması şartıyla, özelliğin güvenli olmayan sürümüne zaten sahiptir. Soru, C # ekibinin yönetilen türleri işleyen güvenli olmayan bir sürüm yapması gerekip gerekmediğidir. Bunu yapmak geliştiricinin korkunç hatalar yazmasını kolaylaştırırsa, olmayacak. C #, korkunç hatalar yazmayı kolaylaştıran bir dil olan C ++ değildir. C # tasarım açısından güvenlidir.
Eric Lippert

21

Bir değer türüne başvuru döndüren yöntemlerden bahsediyorsunuz. Bildiğim tek yerleşik C # örneği, bir değer türünün dizi erişimcisi:

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

ve şimdi bu yapıdan bir dizi oluşturun:

var points = new Point[10];
points[0].X = 1;
points[0].Y = 2;

Bu durumda points[0], dizi indeksleyici yapıya bir başvuru döndürür. Aynı "başvuru döndürme" davranışına sahip olan kendi dizinleyicinizi (örneğin, özel bir koleksiyon için) yazmak imkansızdır .

C # dilini tasarlamadım, bu yüzden onu desteklememenin arkasındaki tüm mantığı bilmiyorum, ama kısa cevabın olabileceğini düşünüyorum: onsuz iyi geçinebiliriz.


8
Tom bir değişkene başvuru döndüren yöntemleri soruyor . Değişkenin değer türü olmasına gerek yoktur, ancak elbette insanların ref-iade yöntemleri istediklerinde genellikle istedikleri şey budur. Aksi takdirde, büyük analiz; C # dilinde, karmaşık bir ifadenin kullanıcının işleyebileceği bir değişkene ref oluşturduğu tek yerin dizi indeksleyicisi olduğu doğrudur. (Ve tabii ki üye erişimi operatörünün bir alıcı ve bir alanın arasında, ama bu oldukça açıkçası bir değişkene bir erişim var "".)
Eric Lippert

1

Her zaman böyle bir şey yapabilirsiniz:

public delegate void MyByRefConsumer<T>(ref T val);

public void DoSomethingWithValueType(MyByRefConsumer<int> c)
{
        int x = 2;
        c(ref x);
        //Handle potentially changed x...
}

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.