2016-06-17 15 views
5

considerare le seguenti due metodi di estensione:Perché il compilatore sceglie di eseguire l'override con IEnumerable su IEnumerable <T>?

using System; 
using System.Collections.Generic; 
using System.Linq; 

public static class Extensions 
{ 
    public static bool Contains(this IEnumerable self, object obj) 
    { 
     foreach (object o in self) 
     { 
      if (Object.Equals(o, obj)) 
      { 
       return true; 
      } 
     } 
     return false; 
    } 

    public static bool ContainsEither<T>(this IEnumerable<T> self, T arg1, T arg2) 
    { 
     return self.Contains(arg1) || self.Contains(arg2); 
    } 
} 

Quando ho scritto il mio secondo metodo, intendevo per chiamare il metodo generico LINQ Enumerable.Contains<T> (argomenti di tipo desunti dall'uso). Tuttavia, ho scoperto che in realtà è chiamando il primo metodo (mia abitudine Contains() metodo di estensione. Quando io commento il mio metodo Contains(), il secondo metodo compila bene, utilizzando il metodo Enumerable.Contains<T>().

La mia domanda è, perché il compilatore scegliere il mio metodo Contains() con IEnumerable argomento non generico su Enumerable.Contains<T>() con IEnumerable<T> argomento mi aspetterei che scegliere Enumerable.Contains<T>() perché IEnumerable<T> è più derivato di IEnumerable

Update:?.. Grazie a Jon Skeet per la grande risposta sarebbe interessante sapere perché i designer hanno scelto questo modo. Ho passato un po 'di tempo ad esplorare il caso s dove questo sarebbe un problema, ho pensato a uno (concesso un po 'di una portata)

public static double Average(this IEnumerable self) 
{ 
    double sum = 0; 
    long count = 0; 
    foreach (object obj in self) 
    { 
     sum += Convert.ToDouble(obj); 
     nItems++; 
    } 
    return sum/count; 
} 

object[] junk = new object[] { "9000", (byte)99, -8.555, 3154254325345UL }; 
double[] clean = new double[] { 9000, 99, -8.555, 3154254325345 }; 
double junkAvg = junk.Average(); 
double cleanAvg = clean.Average(); // compiler is choosing your Average() method where you'd prefer it to choose Enumerable.Average(IEnumerable<double>) 
+0

generici sono più specifici. –

+0

In generale, in situazioni come questa, la domanda "perché" è generalmente interessante, ma probabilmente più importante è la domanda "come posso renderlo non .....". –

+0

@ LasseV.Karlsen Davvero circa il perché. Potrei aver scritto Enumerable.Contains (...) o Contains (...) –

risposta

20

La mia domanda è, perché il compilatore sceglie il mio metodo contains() con non generico IEnumerable discussione su Enumerable.Contains<T>() con argomento IEnumerable<T>?

Perché è stato trovato nello stesso spazio dei nomi che contiene il metodo che lo chiama. I tipi dichiarati all'interno di tale spazio dei nomi hanno effettivamente la precedenza sui tipi dichiarati negli spazi dei nomi importati.

Dalla specifiche C# 5, sezione 7.6.5.2:

La ricerca di C procede come segue:

  • A partire dalla dichiarazione dello spazio dei nomi che racchiude più vicino, continuando con ogni dichiarazione dello spazio dei nomi che racchiude, e terminando con l'unità di compilazione contenente, vengono fatti tentativi successivi per trovare un candidato insieme di metodi di estensione:
    • Se lo spazio dei nomi specificato o la compilazione n unità contiene direttamente dichiarazioni di tipo non generico Ci con metodi di estensione idonei Mj, quindi l'insieme di tali metodi di estensione è il set candidato.
    • Se gli spazi dei nomi importati utilizzando le direttive dello spazio dei nomi nello spazio dei nomi specificato o nell'unità di compilazione contengono direttamente dichiarazioni di tipo non generico Ci con metodi di estensione idonei Mj, l'insieme di tali metodi di estensione è il set candidato.
  • Se nessun gruppo candidato viene trovato in alcuna dichiarazione di namespace o unità di compilazione, si verifica un errore in fase di compilazione.
Problemi correlati