Bu Sparta mı yoksa değil mi?


121

Aşağıdaki bir mülakat sorusudur. Bir çözüm buldum ama neden işe yaradığından emin değilim.


Soru:

SpartaSınıfı değiştirmeden, MakeItReturnFalsegeri dönüş yapan bir kod yazın false.

public class Sparta : Place
{
    public bool MakeItReturnFalse()
    {
        return this is Sparta;
    }
}

Benim çözümüm: (SPOILER)

public class Place
{
public interface Sparta { }
}

Ama neden yok Spartaiçinde MakeItReturnFalse()başvurmak {namespace}.Place.Spartayerine {namespace}.Sparta?


5
Lütfen Çözüme Spoiler Uyarısı veya başka bir şey ekleyebilir misiniz? O kadar hayal kırıklığına uğradım ki kendi başıma çözme şansım yok. Soru gerçekten şaşırtıcı.
Karolis Kajenas

1
Başlangıçta spoiler etiketlerini ekledim, ancak bu yazının ömrü boyunca topluluk tarafından birçok kez düzenlendi. Bunun için üzgünüm.
budi

1
Bu yazının başlığını beğenmedim; kod golfüne daha uygun. Soruyu gerçekten tanımlayan bir şeye değiştirebilir miyiz?
Supuhstar

3
Sevimli bulmaca. Korkunç bir röportaj sorusu, ama sevimli bir bulmaca. Artık bunun nasıl ve neden çalıştığını bildiğinize göre, bu daha zor sürüme bir göz atmalısınız: stackoverflow.com/q/41319962/88656
Eric Lippert

Yanıtlar:


117

Ama neden yok Spartaiçinde MakeItReturnFalse()başvurmak {namespace}.Place.Spartayerine {namespace}.Sparta?

Temel olarak, çünkü ad arama kurallarının söylediği budur. C # 5 belirtiminde, ilgili adlandırma kuralları bölüm 3.8'de ("Ad alanı ve tür adları") bulunmaktadır.

İlk iki madde işareti - kısaltılmış ve açıklamalı - okuyun:

  • Ad-alanı-veya-tür-adı formda Iveya formdaysa I<A1, ..., AK> [bizim durumumuzda K = 0] :
    • K sıfırsa ve ad alanı veya tür adı genel bir yöntem bildiriminde görünüyorsa [hayır, genel yöntem yok]
    • Aksi takdirde, ad alanı veya tür adı bir tür bildiriminde görünürse, o tür bildiriminin örnek türünden başlayarak ve her çevreleyen sınıfın örnek türüyle devam ederek her örnek türü için T (§10.3.1) veya struct bildirimi (varsa):
      • Eğer Ksıfır ve beyanı Tadıyla bir tür parametresi içerir I, daha sonra isim ya da tipi adı bu tür parametresine karşılık gelir. [Hayır]
      • Aksi takdirde, ad-alanı-veya-tür-adı, tür bildiriminin gövdesinde görünürse ve T veya temel türlerinden herhangi biri, ad Ive Ktür parametrelerine sahip iç içe erişilebilir bir tür içeriyorsa , ad alanı veya tür adı buna başvurur. verilen tür bağımsız değişkenleriyle oluşturulan tür.[Bingo!]
  • Önceki adımlar başarısız olduysa N, her ad alanı için ad alanı veya tür adının geçtiği ad alanından başlayarak, her bir ad alanını çevreleyen (varsa) devam eden ve genel ad alanıyla biten aşağıdaki adımlar değerlendirilir. bir varlık bulunana kadar:
    • Eğer Ksıfırdır ve Ibir ad adıdır N, o zaman ... [Evet, bu olur başarılı]

Bu son madde işareti noktası Yani alır ne Sparta sınıfını İlk kurşun şey bulamazsa ... ama taban sınıfı ne zaman Placebir arabirim tanımlar Sparta, bu tespit alır önce düşündüğümüz Spartasınıfı.

İç içe türü Place.Spartabir arabirim yerine bir sınıf yaparsanız, yine de derler ve geri döner false- ancak derleyici bir uyarı verir çünkü bir örneğinin Spartaasla sınıfın bir örneği olmayacağını bilir Place.Sparta. Aynı şekilde, Place.Spartabir arabirimi tutup Spartasınıfı sealedyaparsanız, hiçbir Spartaörnek arabirimi uygulayamayacağı için bir uyarı alırsınız .


2
Başka bir rastgele gözlem: Orijinal Spartasınıfı kullanarak this is Placegeri döner true. Ancak, ekleme public interface Place { }için Spartasınıfa neden this is Placegetiri false. Başımı döndürüyor.
budi

@budi: Doğru, çünkü yine önceki madde işareti Placearayüz olarak buluyor .
Jon Skeet

22

Bir ismi değerine çözümlerken, belirsizlikleri çözmek için tanımın "yakınlığı" kullanılır. Hangi tanım "en yakın" ise, seçilmiş olanıdır.

Arayüz Sparta, bir temel sınıf içinde tanımlanmıştır. Sınıf Sparta, içeren ad alanında tanımlanır. Bir temel sınıf içinde tanımlanan şeyler, aynı ad alanında tanımlanan şeylerden "daha yakındır".


1
Ad arama eğer Ve hayal değil bu şekilde çalışır. Daha sonra, herhangi biri aynı ada sahip bir üst düzey sınıf eklerse, bir iç sınıf içeren çalışma kodu bozulur.
dan04

1
@ dan04: Ama bunun yerine, çalışma kod gelmez herkes eklemek olursa iç içe geçmiş bir sınıf içerir kırıldığında iç içe bir üst düzey sınıf olarak aynı adla bir sınıf. Yani tam olarak tam bir "kazanma" senaryosu değil.
Jon Skeet

1
@JonSkeet Böyle iç içe geçmiş bir sınıf eklemenin, çalışma kodunun etkilenmesi için makul nedenlerin olduğu bir alandaki değişiklik olduğunu ve değişiklikleri gözlemlediğini söyleyebilirim. Tamamen ilgisiz bir üst düzey sınıf eklemek çok daha uzaktır.
Angew artık

2
@JonSkeet Bu biraz farklı bir kılıkta sadece Gevrek Temel Sınıf problemi değil mi?
João Mendes

1
@ JoãoMendes: Evet, hemen hemen.
Jon Skeet

1

Güzel soru! Günlük olarak C # yapmayanlar için biraz daha uzun bir açıklama eklemek istiyorum ... çünkü soru genel olarak ad çözümleme sorunlarının iyi bir hatırlatıcısıdır.

Aşağıdaki şekillerde biraz değiştirilmiş orijinal kodu alın:

  • Orijinal ifadedeki gibi karşılaştırmak yerine tür adlarını yazdıralım (ör. return this is Sparta ) .
  • Arayüzü tanımlayalım Athena.PlaceArayüz adı çözümlemesini göstermek üst sınıfta .
  • Ayrıca , her şeyi çok netleştirmek thisiçin Spartasınıfta bağlı olduğu gibi tür adını da yazdıralım .

Kod şuna benzer:

public class Place {
    public interface Athena { }
}

public class Sparta : Place
{
    public void printTypeOfThis()
    {
        Console.WriteLine (this.GetType().Name);
    }

    public void printTypeOfSparta()
    {
        Console.WriteLine (typeof(Sparta));
    }

    public void printTypeOfAthena()
    {
        Console.WriteLine (typeof(Athena));
    }
}

Şimdi bir Spartanesne oluşturuyoruz ve üç yöntemi çağırıyoruz.

public static void Main(string[] args)
    {
        Sparta s = new Sparta();
        s.printTypeOfThis();
        s.printTypeOfSparta();
        s.printTypeOfAthena();
    }
}

Elde ettiğimiz çıktı:

Sparta
Athena
Place+Athena

Ancak, Place sınıfını değiştirir ve Sparta arabirimini tanımlarsak:

   public class Place {
        public interface Athena { }
        public interface Sparta { } 
    }

o zaman bu Sparta- arayüz - ilk önce ad arama mekanizmasına sunulacak ve kodumuzun çıktısı şu şekilde değişecek:

Sparta
Place+Sparta
Place+Athena

Bu nedenle, sayfadaki tür karşılaştırmasını etkili bir şekilde MakeItReturnFalse , ilk önce ad çözümlemesiyle bulunan, süper sınıfta Sparta arayüzünü tanımlayarak, fonksiyon tanımındaki .

Peki C # neden ad çözümlemesinde süper sınıfta tanımlanan arayüzlere öncelik vermeyi seçti? @JonSkeet biliyor! Cevabını okursanız, ad çözümleme protokolünün ayrıntılarını C # olarak alacaksınız.

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.