2012-05-02 8 views
6

Desidero una funzione di aiuto per la ricerca digitata per una raccolta eterogenea: deve restituire una struct o una classe, altrimenti null se l'elemento non viene trovato. Di seguito è riportato un esempio che utilizza una ricerca di raccolta banale, ma potrebbe essere una chiamata al database o qualsiasi altra cosa.Come implementare il metodo di ricerca di una raccolta generica che può restituire una struct di classe o nullable?

Esiste un modo per ottenere questo risultato con una singola firma del metodo?

public T GetClass<T>(string key) where T : class 
    { 
     object o; 
     if (Contents.TryGetValue(key, out o)) 
     { 
      return o as T; 
     } 
     return null; 
    } 

    public T? GetStruct<T>(string key) where T : struct 
    { 
     object o; 
     if (Contents.TryGetValue(key, out o)) 
     { 
      if (o is T) 
      { 
       return (T?) o; 
      } 
     } 
     return null; 
    } 

Quello che ho già provato:

  • Capisco che le restrizioni generiche non possono essere utilizzati per disambiguare sovraccarichi. Quindi non posso semplicemente dare a questi due metodi lo stesso nome.
  • Il reso (Default) T non è un'opzione, poiché 0 è un valore int valido.
  • Ho provato a chiamare con il <int ?> come tipo, ma come discusso, Nullable<T> non è un tipo di riferimento.

C'è un modo per indicare che ho intenzione di restituire un int in scatola?

risposta

3

Il seguente metodo funziona per entrambe le classi e strutture annullabili:

public static T GetValue<T>(string key) 
{ 
    object o; 
    if (Contents.TryGetValue(key, out o)) 
    { 
     if (o is T) 
     { 
      return (T)o; 
     } 
    } 
    return default(T); 
} 

utilizzati:

int? result1 = GetValue<int?>("someInt"); 
string result2 = GetValue<string>("someString"); 

Si noti come la ? fa parte di argomento di tipo generico e non definito dal metodo su il tipo di ritorno.

+3

I metà come questo - ma * permette * di chiamare come 'getClass ' e restituire 0 per " trovato con valore 0 "e" non trovato "indistintamente. In effetti, potresti perdere per sbaglio il secondo '?' Nel tuo esempio di utilizzo e ottenere un bug piuttosto sottile. ('result1' dovrebbe * mai * essere nullo, ma ci si aspetterebbe che sia) –

+0

Penso che il valore predefinito per int? è nullo – akatakritos

+0

Ma cosa succede se il valore che si sta cercando è un int regolare: penso che il (o è int?) fallirà bc int? non è un int. – akatakritos

6

Esiste un modo per ottenere questo risultato con una singola firma del metodo?

C'è un modo horrible (veramente orrenda) per farlo utilizzando parametri opzionali in modo che il codice chiamante può guardare lo stesso in entrambi i casi. È icky però.

Opzioni:

  • restituire un Tuple<T, bool> invece di utilizzare la nullità
  • utilizzare un parametro out (come int.TryParse ecc)
  • Utilizzare i nomi dei metodi differenti

noti che segnalando l'assenza di un valore separatamente, è possibile rendere null un risultato "trovato" valido, che a volte può essere utile. Oppure tu potresti vuoi solo garantire che non verrà mai restituito.

Se si desidera veramente utilizzare la nullità, sceglierei l'ultima opzione. Credo che renderà il tuo codice più chiaro comunque. IMO, il sovraccarico dovrebbe essere usato solo quando i metodi corrispondono esattamente a la stessa cosa espressa utilizzando parametri diversi - mentre restituire Nullable<T> come tipo restituito in un caso e T come il tipo restituito nell'altro caso non si può davvero vedere che modo.

1

Questo dovrebbe fare esattamente quello che ti serve. Se il tipo richiesto è di tipo nullable, controlla il tipo sottostante prima di trasmettere.

public static T GetValue<T>(string key) 
{ 
    object o; 
    if (Contents.TryGetValue(key, out o)) 
    { 
     if (o is T || Nullable.GetUnderlyingType(typeof(T)) == o.GetType()) 
     { 
      return (T)o; 
     } 
    } 

    return default(T); 
} 

mio codice di prova:

Contents.Add("a string", "string value"); 
Contents.Add("an integer", 1); 
Contents.Add("a nullable integer", new Nullable<int>(2)); 

// Get objects as the type we originally used. 
Debug.WriteLine(string.Format("GetValue<string>(\"a string\") = {0}", GetValue<string>("a string"))); 
Debug.WriteLine(string.Format("GetValue<int>(\"an integer\") = {0}", GetValue<int>("an integer"))); 
Debug.WriteLine(string.Format("GetValue<int?>(\"a nullable integer\") = {0}", GetValue<int?>("a nullable integer"))); 

// Get objects as base class object. 
Debug.WriteLine(string.Format("GetValue<object>(\"a string\") = {0}", GetValue<object>("a string"))); 
Debug.WriteLine(string.Format("GetValue<object>(\"an integer\") = {0}", GetValue<object>("an integer"))); 
Debug.WriteLine(string.Format("GetValue<object>(\"a nullable integer\") = {0}", GetValue<object>("a nullable integer"))); 

// Get the ints as the other type. 
Debug.WriteLine(string.Format("GetValue<int?>(\"an integer\") = {0}", GetValue<int?>("an integer"))); 
Debug.WriteLine(string.Format("GetValue<int>(\"a nullable integer\") = {0}", GetValue<int>("a nullable integer"))); 

// Attempt to get as a struct that it's not, should return default value. 
Debug.WriteLine(string.Format("GetValue<double>(\"a string\") = {0}", GetValue<double>("a string"))); 

// Attempt to get as a nullable struct that it's not, or as a class that it's not, should return null. 
Debug.WriteLine(string.Format("GetValue<double?>(\"a string\") = {0}", GetValue<double?>("a string"))); 
Debug.WriteLine(string.Format("GetValue<StringBuilder>(\"a string\") = {0}", GetValue<StringBuilder>("a string"))); 

Risultati:

GetValue<string>("a string") = string value 
GetValue<int>("an integer") = 1 
GetValue<int?>("a nullable integer") = 2 

GetValue<object>("a string") = string value 
GetValue<object>("an integer") = 1 
GetValue<object>("a nullable integer") = 2 

GetValue<int?>("an integer") = 1 
GetValue<int>("a nullable integer") = 2 

GetValue<double>("a string") = 0 

GetValue<double?>("a string") = 
GetValue<StringBuilder>("a string") = 
+0

'(oggetto) 42 è int?' È vero. Non c'è bisogno di '|| Nullabile ... 'controlla. – dtb

+0

Dang, a volte odio la magia del compilatore intorno a nullable. Nessuna altra struttura restituirebbe true per ' è '. –

Problemi correlati