2010-10-13 13 views
5

Belki biraz zor, ama nedenini merak ediyorum. System.Core.dll ait System.Linq.Enumerable.cs biz var:Eklenti yöntemleri ve derleme zamanı denetimi

Ben kötü bir şey yapıyorum Benim kod
public static int Count<TSource>(this IEnumerable<TSource> source); 

: Ben CommentedExtensions yorumsuz, ben "diyerek bir derleme hatası alırsınız

namespace Test 
{ 
    public static class Extensions 
    { 
    public static int Count<TSource>(this IEnumerable<TSource> source) 
    { 
     return -1; //evil code 
    } 
    } 

    //commented temporarily 
    //public static class CommentedExtensions 
    //{ 
    // public static int Count<TSource>(this IEnumerable<TSource> source) 
    // { 
    //  return -2; //another evil code 
    // } 
    //} 

    public static void Main(string[] args) 
    { 
    Console.WriteLine(Enumerable.Range(0,10).Count()); // -1, evil code works 
    Console.Read(); 
    } 
} 

bu çağrı belirsiz blabla olduğunu " beklenildiği gibi. Ama bu hatayı ilk defa neden alamadım? Ayrıca belirsiz!

EDIT Başka bir testten sonra, uzantı yöntemleri farklı ad alanlarında olsalar bile derleme hataları almayacağımı, hatta tamamen aynı olduklarını fark ettim. Neden izin verilir? C# içinde belirsiz bir yöntem çağrısı getiriyor.

EDIT2 Aslında iki Count'un IL'de farklı olduğunu biliyorum. Aslında

Enumerable.Count(Enumerable.Range(0,10)) 

aradığını ve benim kötü uzatma yönteminin çağırdığı:

MyExtension.Count(Enumerable.Range(0,10)) 

yüzden farklı. Ama yine de bence kötü bir koku. "Gerçek" uzatma yöntemleri var mı? kötülük davranışını önleyebilir?

cevap

4

Bölüm 7.6.5.

aşağıdaki C [(aday uzantısı yöntemi)] arayışı devam ederken: C# language specification 2 derleyici uzatma yöntemleri kapsamında olduğunu belirler, ve uzatma yöntemleri diğerlerinden önceliklidir olan açıklamaktadır

  • , en yakın kapsayan ad alanı bildirimi ile başlayarak her biri, kapatma ad alanı bildirimi şeklinde devam etmelidir içeren derleme birimine ile biten arda denemeler uzatma yöntemleri aday setini bulmak için yapılır:
    • Eğer verilen ad veya compilat İyon ünitesi, uygun uzatma metotları ile Mj genel olmayan tip beyanları Ci'yi içerir, daha sonra bu uzatma metotları seti,
    • set kümesidir. Ad alanı veya derleme ünitesindeki ad alanı yönergeleri kullanılarak içe aktarılan adlar doğrudan jenerik olmayan tür içeriyorsa Uygun uzantı yöntemleri ile Mj beyanları Mj, daha sonra bu uzatma yöntemlerinin seti aday kümedir. Eğer onları aramak hangi kod ile aynı ad alanında uzatma yöntemleri varsa, bu uzantı yöntemleri seçildiğini Anlamı

. Kapsayan bir ad alanındaki genişletme yöntemleri, içe aktarılan diğer ad alanlarına göre seçilecektir.

2

O C# Bu durumda başka bir ad (XXX) içine ana yöntem taşırsanız IL

.method public hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  27 (0x1b) 
    .maxstack 8 
    IL_0000: nop 
    IL_0001: ldc.i4.0 
    IL_0002: ldc.i4.s 10 
    IL_0004: call  class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, 
                                    int32) 
    IL_0009: call  int32 Test.Extensions::Count<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>) 
    IL_000e: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_0013: nop 
    IL_0014: call  int32 [mscorlib]System.Console::Read() 
    IL_0019: pop 
    IL_001a: ret 
} // end of method Program::Main 

bu örnekte

ilk akım adı uzayda görünüyor görünür derleyici açıkça ikincisi örneğin yi daki yöntemi kullanmak için System.Linq sürümü

namespace Test 
{ 
    public static class Extensions 
    { 
     public static int Count<TSource>(this IEnumerable<TSource> source) 
     { 
      return -1; //evil code 
     } 
    } 

} 

namespace XXX{ 

    public static class Program 
    { 
     public static void Main(string[] args) 
     { 
      Console.WriteLine(Enumerable.Range(0, 10).Count()); // -1, evil code works 
      Console.Read(); 
     } 
    } 
} 


.method public hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  27 (0x1b) 
    .maxstack 8 
    IL_0000: nop 
    IL_0001: ldc.i4.0 
    IL_0002: ldc.i4.s 10 
    IL_0004: call  class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, 
                                    int32) 
    IL_0009: call  int32 [System.Core]System.Linq.Enumerable::Count<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>) 
    IL_000e: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_0013: nop 
    IL_0014: call  int32 [mscorlib]System.Console::Read() 
    IL_0019: pop 
    IL_001a: ret 
} // end of method Program::Main 

yöntemini giderir ou hem ad tanımlanır senin yöntemi kullanmak o zaman yeni bir sınıf oluşturursanız

namespace XXX{ 
    using Test; 
    public static class Program 
    { 
     public static void Main(string[] args) 
     { 
      Console.WriteLine(Enumerable.Range(0, 10).Count()); // -1, evil code works 
      Console.Read(); 
     } 

    } 
} 

.method public hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  27 (0x1b) 
    .maxstack 8 
    IL_0000: nop 
    IL_0001: ldc.i4.0 
    IL_0002: ldc.i4.s 10 
    IL_0004: call  class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, 
                                    int32) 
    IL_0009: call  int32 Test.Extensions::Count<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>) 
    IL_000e: call  void [mscorlib]System.Console::WriteLine(int32) 
    IL_0013: nop 
    IL_0014: call  int32 [mscorlib]System.Console::Read() 
    IL_0019: pop 
    IL_001a: ret 
} // end of method Program::Main 
0

kullanmak ve her iki ad alanlarına usings ekleyip, hata yine orada olmalıdır.

Uzantı yöntemleri, bildirildikleri statik sınıflar tarafından değil, ad alanlarına göre ayırt edilirler. Derleyici, aynı ad alanında iki uzantı yöntemi tanımlanırsa, hangi iki almayı bulabilir.

0

Sanırım, OOP'ların ilkelerine aykırı aynı aşırı yüke sahip iki yöntem yazıyorsunuz. Uzantı yöntemi, kullanımınızla aynı ad alanı kapsamı içinde ise, o zaman System.Core.dll biriyle aynıysa, (kullanım yerinde bulunan en yakın aşırı yük olduğu için) önceliği alır. Her iki ad uzatma uzantısı yöntemleri vardır.

Bunu kanıtlamak için, uzantı yöntem adınızı MyCount1 (...) & MyCount2 (...) olarak değiştirin, daha sonra sizin için çalışmalıdır.

İlgili konular