Neden C # derleyicisi, statik bir yöntemin bir örnek yöntemini çağırdığı kodda hata yapmıyor?


110

Aşağıdaki kodun Foo()bir örnek yöntemini çağıran statik bir yöntemi vardır Bar():

public sealed class Example
{
    int count;

    public static void Foo( dynamic x )
    {
        Bar(x);
    }

    void Bar( dynamic x )
    {
        count++;
    }
}

Hatasız derler * ancak çalışma zamanında bir çalışma zamanı bağlayıcı istisnası oluşturur. Dinamik parametrenin bu yöntemlere kaldırılması, beklendiği gibi bir derleyici hatasına neden olur.

Öyleyse neden dinamik bir parametreye sahip olmak kodun derlenmesine izin veriyor? ReSharper bunu bir hata olarak da göstermiyor.

Visual Studio 2008'de Düzenleme 1: *

Düzenleme 2:sealed bir alt sınıfın statik bir Bar(...)yöntem içermesi olası olduğundan eklendi . Mühürlenmiş sürüm bile, çalışma zamanında örnek yöntemi dışındaki herhangi bir yöntemin çağrılabilmesi mümkün olmadığında derlenir.


8
Çok iyi soru için +1
cuongle

40
Bu bir Eric Lippert sorusudur.
Olivier Jacot-Descombes

3
Jon Skeet'in bu aswell ile ne yapacağını bileceğinden oldukça eminim;) @ OlivierJacot-Descombes
Bin

2
@Olivier, Jon Skeet muhtemelen kodun derlenmesini istedi, bu yüzden derleyici buna izin veriyor :-))
Mike Scott

5
Bu, dynamicgerçekten ihtiyacınız olmadıkça neden kullanmamanız gerektiğinin başka bir örneğidir .
2012'de

Yanıtlar:


71

GÜNCELLEME: Aşağıdaki cevap 2012'de, C # 7.3'ün (Mayıs 2018) tanıtımından önce yazılmıştır . In C # 7.3 yenilikler , bölüm Geliştirilmiş aşırı yük adaylar , madde 1, aşırı yük çözünürlük kuralları statik olmayan aşırı yükler erken atılır, böylece nasıl değiştiğini açıklanmıştır. Yani aşağıdaki cevap (ve tüm bu soru) şimdiye kadar çoğunlukla yalnızca tarihsel ilgiye sahip!


(Ön C # 7.3 :)

Bazı nedenlerden dolayı, aşırı yük çözünürlüğü her zaman statik ve statik olmayan için kontrol etmeden önce en iyi eşleşmeyi bulur . Lütfen bu kodu tüm statik türlerle deneyin:

class SillyStuff
{
  static void SameName(object o) { }
  void SameName(string s) { }

  public static void Test()
  {
    SameName("Hi mom");
  }
}

Bu derlenmeyecektir çünkü en iyi aşırı yükleme, bir string. Ama hey, bu bir örnek yöntemi, yani derleyici şikayet ediyor (ikinci en iyi aşırı yüklemeyi almak yerine).

Ekleme: Bence dynamicOrijinal Soru örneğinin açıklaması, tutarlı olmak için, türler dinamik olduğunda ilk önce en iyi aşırı yüklemeyi de bulduğumuzdur (statik ve olmayan değil, yalnızca parametre numarası ve parametre türleri vb. -static) ve ancak o zaman statik kontrolü yapın. Ancak bu, statik kontrolün çalışma zamanına kadar beklemesi gerektiği anlamına gelir. Dolayısıyla gözlemlenen davranış.

Son ek: Eric Lippert tarafından yazılan bu blog gönderisinden bu komik sırayı neden seçtiklerine dair bazı arka plan bilgileri çıkarılabilir .


İlk soruda aşırı yük yok. Statik bir aşırı yüklemeyi gösteren cevaplar alakalı değildir. "Peki bunu yazdıysanız ..." cevabını vermek geçerli değil çünkü bunu ben yazmadım :-)
Mike Scott

5
@MikeScott Ben sadece sizi C # 'daki aşırı yük çözünürlüğünün her zaman böyle olduğuna ikna etmeye çalışıyorum: (1) Statik / statik olmayanı göz ardı ederek en iyi eşleşmeyi bulun. (2) Artık hangi aşırı yüklemeyi kullanacağımızı biliyoruz, sonra statik olup olmadığını kontrol ediyoruz . Bu nedenle dynamic, dilde tanıtıldığında, C # tasarımcılarının şunu söylediğini düşünüyorum: "(2) bir dynamicifade olduğunda derleme zamanını dikkate almayacağız ." Yani buradaki amacım, çalışma zamanına kadar neden statik ve örneği kontrol etmemeyi seçtiklerine dair bir fikir bulmaktır. Bu kontrolün bağlama zamanında gerçekleştiğini söyleyebilirim .
Jeppe Stig Nielsen

yeterince adil, ancak yine de bu durumda derleyicinin örnek yöntemine yapılan çağrıyı neden çözemediğini açıklamıyor. Başka bir deyişle, derleyicinin çözünürlüğü yapma şekli basittir - çağrıyı çözememe ihtimalinin olmadığı örneğim gibi basit durumu tanımıyor. İroni şudur: dinamik parametreye sahip tek bir Bar () yöntemine sahip olarak, derleyici daha sonra bu tek Bar () yöntemini yok sayar.
Mike Scott

45
C # derleyicisinin bu bölümünü yazdım ve Jeppe haklı. Lütfen bunu oylayın. Aşırı yük çözümlemesi, belirli bir yöntemin statik mi yoksa örnek yöntemi mi olduğunun kontrol edilmesinden önce gerçekleşir ve bu durumda aşırı yük çözümlemesini çalışma zamanına ve dolayısıyla statik / örnek denetimini çalışma zamanına kadar erteleriz. Ek olarak, derleyici dinamik hataları statik olarak bulmak için "en iyi çabayı" gösterir ve bu kesinlikle kapsamlı değildir.
Chris Burrows

30

Foo, dinamik olan bir "x" parametresine sahiptir, yani Bar (x) dinamik bir ifadedir.

Örnek'in aşağıdaki gibi yöntemlere sahip olması tamamen mümkündür:

static Bar(SomeType obj)

Bu durumda doğru yöntem çözülecektir, bu nedenle Bar (x) ifadesi tamamen geçerlidir. Bir örnek yöntemi Bar (x) olduğu gerçeği alakasızdır ve dikkate alınmaz bile: Tanım gereği , Bar (x) dinamik bir ifade olduğundan, çözünürlüğü çalışma zamanına erteledik.


14
ancak örnek Bar yöntemini çıkardığınızda, artık derlenmez.
Justin Harvey

1
@Justin ilginç - bir uyarı mı? Veya bir hata? Her iki durumda da, yalnızca yöntem grubuna kadar doğrulanıyor olabilir ve tam aşırı yük çözümlemesini çalışma zamanına bırakabilir.
Marc Gravell

1
@Marc, başka bir Bar () yöntemi olmadığı için soruyu cevaplamıyorsun. Aşırı yüklemesi olmayan tek bir Bar () yöntemi olduğu için bunu açıklayabilir misiniz? Başka bir yöntemin çağrılması mümkün olmadığında neden çalışma zamanını ertelesiniz? Yoksa var mı? Not: Hala derlenen sınıfı mühürlemek için kodu düzenledim.
Mike Scott

1
çalışma zamanına neden ertelemek gelince @ Mike: yani, çünkü neyi dinamik araçları
Marc Gravell

2
@Mike imkansız mesele değil; önemli olan gerekli olup olmadığıdır . Dinamik ile ilgili tüm nokta, derleyicinin işi olmamasıdır.
Marc Gravell

9

"Dinamik" ifade çalışma zamanı sırasında bağlanacaktır, bu nedenle doğru imzayla veya bir örnek yöntemiyle statik bir yöntem tanımlarsanız, derleyici bunu kontrol etmez.

"Doğru" yöntem çalışma sırasında belirlenecektir. Derleyici, çalışma süresi boyunca orada geçerli bir yöntem olup olmadığını bilemez.

"Dynamic" anahtar sözcüğü, Yöntemin çalışma zamanı sırasında bile herhangi bir zamanda tanımlanabildiği dinamik ve betik dilleri için tanımlanmıştır. Çılgın şeyler

Burada ints işleyen ancak dizge içermeyen bir örnek, yöntem örnek üzerindedir.

class Program {
    static void Main(string[] args) {
        Example.Foo(1234);
        Example.Foo("1234");
    }
}
public class Example {
    int count;

    public static void Foo(dynamic x) {
        Bar(x);
    }

    public static void Bar(int a) {
        Console.WriteLine(a);
    }

    void Bar(dynamic x) {
        count++;
    }
}

İşlenemeyen tüm "yanlış" çağrıları işlemek için bir yöntem ekleyebilirsiniz

public class Example {
    int count;

    public static void Foo(dynamic x) {
        Bar(x);
    }

    public static void Bar<T>(T a) {
        Console.WriteLine("Error handling:" + a);
    }

    public static void Bar(int a) {
        Console.WriteLine(a);
    }

    void Bar(dynamic x) {
        count++;
    }
}

Örneğinizdeki çağıran kod, Example.Foo (...) yerine Example.Bar (...) olması gerekmez mi? Örneğinizde Foo () alakasız değil mi? Örneğinizi gerçekten anlamıyorum. Statik genel yöntemin eklenmesi neden bir soruna neden olur? Cevabınızı bir seçenek olarak vermek yerine bu yöntemi içerecek şekilde düzenleyebilir misiniz?
Mike Scott

ancak gönderdiğim örnekte yalnızca tek bir örnek yöntemi var ve aşırı yükleme yok, bu nedenle derleme sırasında çözülebilecek olası statik yöntemler olmadığını biliyorsunuz. Yalnızca en az bir tane eklerseniz durum değişir ve kod geçerli olur.
Mike Scott

Ancak bu örnekte hala birden fazla Bar () yöntemi bulunmaktadır. Örneğimde sadece bir yöntem var. Dolayısıyla herhangi bir statik Bar () yöntemini çağırma imkanı yoktur. Çağrı, derleme zamanında çözülebilir.
Mike Scott

@Mike olabilir! =; dinamik ile bunu yapmak gerekli değildir
Marc Gravell

@MarcGravell, lütfen açıklayınız?
Mike Scott
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.