Belirsizliğe dahil olmayacaksa, bir yöntem eklemek neden belirsiz bir çağrı ekler?


112

Bu sınıfa sahibim

public class Overloaded
{
    public void ComplexOverloadResolution(params string[] something)
    {
        Console.WriteLine("Normal Winner");
    }

    public void ComplexOverloadResolution<M>(M something)
    {
        Console.WriteLine("Confused");
    }
}

Ben böyle dersem:

        var blah = new Overloaded();
        blah.ComplexOverloadResolution("Which wins?");

Normal WinnerKonsola yazar .

Ancak başka bir yöntem eklersem:

    public void ComplexOverloadResolution(string something, object somethingElse = null)
    {
        Console.WriteLine("Added Later");
    }

Şu hatayı alıyorum:

Çağrı, aşağıdaki yöntemler veya özellikler arasında belirsizdir:> ' Overloaded.ComplexOverloadResolution(params string[])' ve ' Overloaded.ComplexOverloadResolution<string>(string)'

Bir yöntem eklemenin bir çağrı belirsizliği getirebileceğini anlayabiliyorum, ancak bu, zaten var olan iki yöntem arasında bir belirsizlik (params string[])ve <string>(string)! Açıktır ki, belirsizliğe dahil olan iki yöntemden hiçbiri yeni eklenen yöntem değildir, çünkü birincisi bir parametredir ve ikincisi bir geneldir.

Bu bir hata mı? Spesifikasyonun hangi kısmı bunun böyle olması gerektiğini söylüyor?


2
Sanmıyorum 'Overloaded.ComplexOverloadResolution(string)'atıfta <string>(string)yöntemle; Sanırım (string, object)hiçbir nesne sağlanmayan yöntemi ifade ediyor .
phoog

1
@phoog Oh, bu veri StackOverflow tarafından kesildi çünkü bu bir etiket, ancak hata mesajında ​​şablon belirleyicisi var. Tekrar ekliyorum.
McKay

beni yakaladın! Cevabımda şartnamenin ilgili bölümlerine atıfta bulundum, ancak son yarım saati okuyup anlamak için harcamadım!
phoog

@phoog, spesifikasyonun bu kısımlarına baktığımda, kendisinden ve başka bir yöntemden başka yöntemlere belirsizlik getirme hakkında hiçbir şey görmüyorum, iki başka yöntem değil.
McKay

Bunun sadece taş-kağıt-makas olduğu aklıma geldi : iki farklı değerden oluşan herhangi bir setin bir galibi vardır, ancak üç değerin tam setinde yoktur.
phoog

Yanıtlar:


107

Bu bir hata mı?

Evet.

Tebrikler, aşırı yük çözümünde bir hata buldunuz. Hata C # 4 ve 5'te çoğalır; anlamsal analizörün "Roslyn" versiyonunda yeniden üretilmez. C # 5 test ekibini bilgilendirdim ve umarım bunu son sürümden önce araştırıp çözebiliriz. (Her zaman olduğu gibi söz yok.)

Doğru bir analiz izler. Adaylar:

0: C(params string[]) in its normal form
1: C(params string[]) in its expanded form
2: C<string>(string) 
3: C(string, object) 

Aday sıfır açıkça uygulanamaz çünkü stringdönüştürülemez string[]. Üç kalıyor.

Üçü arasında benzersiz bir en iyi yöntem belirlemeliyiz. Bunu kalan üç adayın ikili karşılaştırmalarını yaparak yapıyoruz. Böyle üç çift var. İhmal edilen isteğe bağlı parametreleri çıkardığımızda hepsinin aynı parametre listeleri var , bu da şartnamenin 7.5.3.2 bölümünde açıklanan gelişmiş eşitlik bozma turuna gitmemiz gerektiği anlamına geliyor.

Hangisi daha iyi, 1 mi 2 mi? İlgili eşitliği bozan şey, jenerik bir yöntemin her zaman jenerik olmayan bir yöntemden daha kötü olmasıdır. 2 1'den daha kötüdür. Yani 2 kazanan olamaz.

Hangisi daha iyi, 1 mi 3 mü? İlgili eşitliği bozan şey şudur: Yalnızca genişletilmiş biçiminde uygulanabilen bir yöntem, her zaman normal biçiminde uygulanabilen bir yöntemden daha kötüdür. Bu nedenle 1, 3'ten daha kötüdür. Yani 1 kazanan olamaz.

Hangisi daha iyi, 2 mi, 3 mü? İlgili eşitliği bozan şey, jenerik bir yöntemin her zaman jenerik olmayan bir yöntemden daha kötü olmasıdır. 2, 3'ten daha kötüdür. Yani 2 kazanan olamaz.

Birden fazla uygun aday arasından seçilmek için bir adayın (1) yenilmemiş olması, (2) en az bir diğer adayı geçmesi ve (3) ilk iki özelliğe sahip olan benzersiz aday olması gerekir. Üçüncü aday başka hiçbir aday tarafından mağlup edilmez ve en az bir diğer adayı yener; bu mülke sahip tek adaydır. Bu nedenle üçüncü aday benzersiz en iyi adaydır . Kazanmalı.

Tuhaf bir hata mesajı verdiğini doğru bir şekilde not ettiğiniz gibi, sadece C # 4 derleyicisi yanlış anlamıyor. Derleyicinin aşırı yük çözümleme analizini yanlış anlaması biraz şaşırtıcı. Hata mesajını yanlış anlaması tamamen şaşırtıcı değildir; "belirsiz yöntem" hatası buluşsal yöntemi, en iyi yöntem belirlenemezse, temelde aday kümeden herhangi iki yöntemi seçer. Aslında varsa, "gerçek" belirsizliği bulmakta pek iyi değil.

Neden böyle olduğu sorulabilir. "Kesinlikle belirsiz" olan iki yöntem bulmak oldukça zordur çünkü "daha iyi" ilişkisi geçişsizdir . Aday 1'in 2'den daha iyi, 2'nin 3'ten daha iyi ve 3'ün 1'den daha iyi olduğu durumlarla karşılaşmak mümkündür. Böyle durumlarda ikisini "belirsiz olanlar" olarak seçmekten daha iyisini yapamayız.

Roslyn için bu buluşsal yöntemi geliştirmek isterim, ancak bu düşük bir önceliktir.

(Okuyucu için alıştırma: "Açıklık ilişkisinin geçişsiz olduğu bir dizi n öğenin benzersiz en iyi üyesini belirlemek için doğrusal bir zaman algoritması tasarlayın", bu takım için röportaj yaptığım gün sorulan sorulardan biriydi. çok zor bir algoritma; bir şans verin.)

C # 'a isteğe bağlı argümanlar eklemeye bu kadar uzun süre devam etmemizin nedenlerinden biri, aşırı yük çözüm algoritmasına getirdiği karmaşık belirsiz durumların sayısıydı; görünüşe göre doğru anlamadık.

Takip etmek için bir Connect sorununa girmek isterseniz, çekinmeyin. Sadece dikkatimizi çekmek istiyorsanız, yapıldığını düşünün. Önümüzdeki yıl testlerle devam edeceğim.

Bunu dikkatime sunduğun için teşekkürler. Hata için özür dileriz.


1
Cevabınız için teşekkürler. "1, 2'den daha kötü" dediniz, ancak yöntem 1 ve 2'ye sahipsem, yöntem 1'i seçer mi?
McKay

@McKay: Oooops, haklısın, bunu tersine söyledim. Metni düzelteceğim.
Eric Lippert

1
Yarım haftası bile kalmadığı için "yılın geri kalanı" ifadesini okumak biraz tuhaf geliyor :)
BoltClock

2
@BoltClock gerçekten, "yılın geri kalanını terk etmek" ifadesi bir gün izin anlamına geliyor.
phoog

1
Ben öyle düşünüyorum. Okuduğum "3) İlk iki özelliği vardır benzersiz aday olmak" olarak "tek (yenilmedi olduğunu adayı ve atım, en azından bir diğer aday) olmak" . Ama en son yorumunuz beni "(yenilmeyen tek aday olun) ve en az bir diğer adayı yener" diye düşündürüyor . İngilizce, gruplama sembollerini gerçekten kullanabilir. İkincisi doğruysa, tekrar anlıyorum.
default.kramer

5

Spesifikasyonun hangi kısmı bunun böyle olması gerektiğini söylüyor?

Bölüm 7.5.3 (aşırı yük çözümü), bölüm 7.4 (üye arama) ve 7.5.2 (tür çıkarımı) ile birlikte.

Özellikle bölüm 7.5.3.2'ye (daha iyi işlev üyesi), kısmen "karşılık gelen bağımsız değişkenler olmayan isteğe bağlı parametreler parametre listesinden kaldırılır" ve "M (p) genel olmayan bir yöntemse, M (q) genel bir yöntemse M (p), M (q) 'dan daha iyidir. "

Bununla birlikte, şartnamenin bu bölümlerini, uyumlu olup olmadığına karar vermek bir yana, şartnamenin hangi bölümlerinin bu davranışı kontrol ettiğini bilebilecek kadar tam olarak anlamıyorum.


Ancak bu, bir üye eklemenin neden zaten var olan iki yöntem arasında bir belirsizliğe neden olduğunu açıklamıyor.
McKay

@McKay yeterince adil (düzenlemeye bakın). Eric
Lippert'in

1
Bunlar şartnamenin doğru kısımları. Sorun, bunun böyle olmaması gerektiğini söylemeleridir !
Eric Lippert

3

Bazı yöntemlerde ilk parametrenin adını değiştirerek ve atamak istediğiniz parametreyi belirterek bu belirsizliği önleyebilirsiniz.

bunun gibi :

public class Overloaded
{
    public void ComplexOverloadResolution(params string[] somethings)
    {
        Console.WriteLine("Normal Winner");
    }

    public void ComplexOverloadResolution<M>(M something)
    {
        Console.WriteLine("Confused");
    }

    public void ComplexOverloadResolution(string something, object somethingElse = null)
    {
        Console.WriteLine("Added Later");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Overloaded a = new Overloaded();
        a.ComplexOverloadResolution(something:"asd");
    }
}

Oh, bu kodun kötü olduğunu biliyorum ve bunun etrafında çalışmanın birkaç yolu var, soru "derleyici neden bu şekilde davranıyor?"
McKay

1

paramsİlk yönteminizden kaldırırsanız , bu olmaz. Birinci ve üçüncü yöntemin her iki geçerli çağrısı var ComplexOverloadResolution(string), ancak ilk yönteminiz varsa public void ComplexOverloadResolution(string[] something)belirsizlik olmayacak.

Bir parametre için değer sağlamak object somethingElse = null, onu isteğe bağlı parametre haline getirir ve bu nedenle, bu aşırı yüklemeyi çağırırken belirtilmesi gerekmez.

Düzenleme: Derleyici burada bazı çılgın şeyler yapıyor. Üçüncü yönteminizi ilk yönteminizden sonra kodda taşırsanız, doğru şekilde raporlanır. Görünüşe göre ilk iki aşırı yüklemeyi alıyor ve doğru olanı kontrol etmeden onları rapor ediyor.

'ConsoleApplication1.Program.ComplexOverloadResolution (parametreler dizesi [])' ve 'ConsoleApplication1.Program.ComplexOverloadResolution (dize, nesne)'

Düzenleme2: Yeni bulgu. Yukarıdaki üç yöntemden herhangi bir yöntemi çıkarmak, ikisi arasında hiçbir belirsizlik yaratmayacaktır. Öyleyse öyle görünüyor ki, çatışma, sıraya bakılmaksızın, yalnızca mevcut üç yöntem varsa ortaya çıkar.


Ancak bu, bir üye eklemenin neden zaten var olan iki yöntem arasında bir belirsizliğe neden olduğunu açıklamıyor.
McKay

Belirsizlik birinci ve üçüncü yönteminiz arasında olur, ancak derleyici neden diğer ikisini rapor ediyor?
Tomislav Markovski

Ama ikinci yöntemi kaldırırsam, bir belirsizliğim olmaz, başarılı bir şekilde üçüncü yöntemi çağırır. Dolayısıyla, derleyicinin birinci ve üçüncü yöntemler arasında bir belirsizliği varmış gibi görünmüyor.
McKay

Düzenlememe bakın. Çılgın derleyiciler.
Tomislav Markovski

Aslında, sadece iki yöntemin herhangi bir kombinasyonu belirsizlik yaratmaz. Bu çok tuhaf. Edit2.
Tomislav Markovski

1
  1. Eğer yazarsan

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");

    ya da sadece yaz

    var blah = new Overloaded();
    blah.ComplexOverloadResolution();

    o olacak biter aynı yöntem haline yöntemde,

    public void ComplexOverloadResolution(params string[] something

    Bu, hiçbir parametre belirtilmediğindeparams de ondan en iyi eşleşmeyi sağlayan anahtar kelimenin nedenidir.

  2. Bunun gibi yeni bir yöntem eklemeye çalışırsanız

    public void ComplexOverloadResolution(string something)
    {
        Console.WriteLine("Added Later");
    }

    Bir parametreyle aramanız için mükemmel bir eşleşme olduğu için bu yöntemi mükemmel bir şekilde derleyecek ve çağıracaktır string. O zaman çok daha güçlü params string[] something.

  3. Yaptığın gibi ikinci yöntemi ilan ediyorsun

    public void ComplexOverloadResolution(string something, object something=null);

    Derleyici, ilk yöntem ile bu arasında tam bir kafa karışıklığı içinde atlar, sadece bir tane ekledi. Çünkü şimdi aramanızın hangi işlevi yapması gerektiğini bilmiyor

    var blah = new Overloaded();
    blah.ComplexOverloadResolution("Which wins?");

    Aslında, aşağıdaki kod gibi, çağrıdan string parametresini kaldırırsanız, her şey doğru bir şekilde derlenir ve eskisi gibi çalışır.

    var blah = new Overloaded();
    blah.ComplexOverloadResolution(); // will be ComplexOverloadResolution(params string[] something) function called here, like a best match.

İlk olarak, aynı olan iki çağrı durumu yazarsınız. Yani tabii ki aynı yönteme mi girecek yoksa farklı bir şey mi yazmak istediniz?
McKay

Ama yine, cevabınızın geri kalanını doğru anlıyorsam, derleyicinin söylediğini okumuyorsunuz, yani ilk ve ikinci yöntemler arasındaki kafa karışıklığı, yeni eklediğim üçüncü yöntem değil.
McKay

Ah teşekkürler. Ancak bu yine de yazınıza ikinci yorumumda bahsettiğim sorunu bırakıyor.
McKay

Daha açık olmak gerekirse, "Derleyici, ilk yöntem ile bu arasında tam bir kafa karışıklığı atlıyor, yeni bir tane eklendi." Ama değil. Diğer iki yönteme kafa karıştırıyor. Params yöntemi ve genel yöntem.
McKay

@McKay: stateKesin olmak gerekirse, tek veya çift değil, belirli 3 fonksiyona sahip olmak kafa karışıklığına atlıyor . Aslında , bir sorunu çözmek için bunlardan herhangi birini yorumlamak yeterlidir . Mevcut fonksiyonlar arasında en iyi eşleşme params, ikincisinin genericsparametreli olmasıdır, üçüncüyü eklediğimizde bir setfonksiyonda karışıklık yaratır . Bence, büyük olasılıkla derleyici tarafından üretilen açık bir hata mesajı değil.
Tigran
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.