2014-04-22 19 views
5

Aşağıdaki program derlenmiyor, çünkü hatayla birlikte derleyici, yöntemi tek bir T parametresiyle çözüm olarak seçiyor, çünkü bu hata List<T>'un genel kısıtlamalarına uymadığından başarısız oluyor. tekli T. Derleyici, kullanılabilecek başka bir yöntem olduğunu algılamaz. Tek T yöntemini kaldırırsam, derleyici birçok nesne için yöntemi doğru olarak bulur.Genel uzantı yöntemi çözünürlüğü başarısız oluyor

Jenerik yöntem çözünürlüğü, JonSkeet here ve Eric Lippert here'dan başka iki blog yazısı okudum, ancak sorunumu çözmek için bir açıklama veya yol bulamadım.

Açıkçası, farklı isimlere sahip iki yöntemin olması işe yarayacaktı, ancak bu durumlar için tek bir yöntemin olması gerçeğini beğeniyorum. Dmitry önerildiği gibi ikincisi aşağıdaki şekilde arayarak,

public static void Method(this SomeInterface parameter) { /*...*/ } 

Veya:

instances.Method<SomeImplementation>(); 
namespace Test 
{ 
    using System.Collections.Generic; 

    public interface SomeInterface { } 

    public class SomeImplementation : SomeInterface { } 

    public static class ExtensionMethods 
    { 
    // comment out this line, to make the compiler chose the right method on the line that throws an error below 
    public static void Method<T>(this T parameter) where T : SomeInterface { } 

    public static void Method<T>(this IEnumerable<T> parameter) where T : SomeInterface { } 
    } 

    class Program 
    { 
    static void Main() 
    { 
     var instance = new SomeImplementation(); 
     var instances = new List<SomeImplementation>(); 

     // works 
     instance.Method(); 

     // Error 1 The type 'System.Collections.Generic.List<Test.SomeImplementation>' 
     // cannot be used as type parameter 'T' in the generic type or method 
     // 'Test.ExtensionMethods.Method<T>(T)'. There is no implicit reference conversion 
     // from 'System.Collections.Generic.List<Test.SomeImplementation>' to 'Test.SomeInterface'. 
     instances.Method(); 

     // works 
     (instances as IEnumerable<SomeImplementation>).Method(); 
    } 
    } 
} 
+0

'instances.Method ();'? – Dmitry

+0

@Dmitry gerçekten işe yarayacak. Birkaç şeyi test edeceğim. – nvoigt

cevap

6

Yöntem çözünürlüğü o closer is better söylüyor. Tam kurallar için blog gönderisine bakın.

Yakın ne anlama geliyor? Derleyici, tam eşleşmeyi bulabilir, eğer bir nedenle bulamazsa, bir sonraki olası uyumlu yöntemleri ve benzerlerini bulabilir.

İlk olarak, SomeInterface kısıtlamasını kaldırarak bu yöntemi derleyin.

public static class ExtensionMethods 
{ 
    public static void Method<T>(this T parameter) //where T : SomeInterface 
    { } 

    public static void Method<T>(this IEnumerable<T> parameter) //where T : SomeInterface 
    { } 
} 

Şimdi derleyici derlemek için mutlu olduğunu ve her iki yöntem Method(T) yerine Method(IEnumerable<T>) Gidiyor çağrıları dikkate yoktur. Neden?

Method(T), parametre olarak herhangi bir türü alabilen ve ayrıca herhangi bir dönüştürme gerektirmeyen anlamda daha yakın olduğundan.

Neden Method(IEnumerable<T>) daha yakın değil?

Eğer List<T> olarak değişkenin derleme zamanı türüne sahip çünkü, bu yüzden List<T> den IEnumerable<T> bir referans dönüşüm ihtiyacı var. Hangisi daha yakın ama hiç dönüşüm yapmamaktan uzak.

Sorunuza geri dön.

Neden instances.Method(); derleme yapmıyor?

Yine, böylece besbelli yakın değil biz bazı referans dönüşümüne ihtiyaç Method(IEnumerable<T>) kullanmayı daha önceden söyledi. Şimdi çok yakın olan tek bir yöntemle kalıyoruz. Method<T>. Ama sorun SomeInterface ile kısıtlı ve açıkça List<SomeImplementation>()SomeInterface'a dönüştürülebilir değil. derleyici yakın aşırı seçer sonra jenerik kısıtlamaları kontrol

sorunu (tahmin ediyorum) olduğu olmuyor. Bu, bu durumda seçilen en iyi aşırı yükü geçersiz kılar.

Kolayca çalışmak ve şimdi neden anlamak gerekir IEnumerable<SomeImplementation> değişkenin statik türü değiştirerek bunu düzeltmek olabilir.

IEnumerable<SomeImplementation> instances = new List<SomeImplementation>(); 
+0

Tamam, aslında benim sorunu çözmez ki (hala gerekecek (veya gerçekten yöntem çözünürlükte bir hata olduğunu gösterebilir) bu davranışı üzerinde bazı ışık tutabilir İnsanlara sürpriz yapmadan farklı bir isim verin), ama en azından oldukça iyi bir açıklama * neden *. – nvoigt

1

Eğer aynı davranması gerektiği gibi, jenerik olmadan ilk birini uygulamayı denediniz

Ama burada her çağrıya <SomeImplementation> eklemem gerekiyor ...

+0

Bu iyi bir fikir. Belki de benim örneğimi genişletmeliydim. Gerçek projemizde T, jenerik kullanmamızın nedeni olan * iki * farklı arayüzün uygulanması ile sınırlıdır. – nvoigt

+0

Ah, tamam. Bu durumda benim fikrim çalışmıyor (ancak bu durum için çalışacak - sadece test) ... – ChrFin

+0

Ben bir çok şey denedik ve ya da yöntemini çağırarak ek kod olmadan her iki durumda da jenerik çalışmak için alamadım ve IMO çalışması gerekir - belki başkası ... – ChrFin

1
  1. Bunu istemediğini bilmeme rağmen, yöntem isimlerinin aynı olması gerektiğini düşünmeniz gerektiğini düşünüyorum. Aynı ismin bir örnek üzerinde nasıl davrandığını ve bu örneklerin nasıl toplandığını göremiyorum. Örneğin, yöntem adınız T için Shoot ise, diğer yöntem ShootThemAll veya benzeri bir şey gibi gelmelidir.

  2. Dilersen başka atama biraz farklı yapmalıdır: Son bir seçenek olarak

    IEnumerable<SomeImplementation> instances = new List<SomeImplementation>(); 
    instances.Method(); //now this should work 
    
  3. Dimitry yorumlarda söylediği gibi açıkça tür bağımsız değişkeni belirtmek gerekir.

    instances.Method<SomeImplementation>();