2010-11-01 15 views
28

Ho letto un eccellente articolo su MSDN relativo a Generics in C#.Perché utilizzare i vincoli generici in C#

La domanda che mi è venuta in mente è stata: perché dovrei usare limiti generici?

Per esempio, se io uso il codice come questo:

public class MyClass<T> where T : ISomething 
{ 
} 

non riesco a cambiare tutti i riferimenti di T in questa classe con ISomething?

Qual è il vantaggio dell'utilizzo di questo approccio?

+0

http://msdn.microsoft.com/en-us/library/d5x73970.aspx – zebrabox

risposta

47

Si chiede, "non è possibile cambiare TUTTI i riferimenti di T in questa classe con ISomething?" Quindi penso che si intende per confrontare:

public class MyClass<T> where T : ISomething 
{ 
    public T MyProperty { get; set; } 
} 

Con:

public class MyClass 
{ 
    public ISomething MyProperty { get; set; } 
} 

Nel secondo esempio, MyProperty è garantita solo per essere un esempio di ISomething. Nel primo esempio, MyProperty è qualsiasi sia T, anche se si tratta di un sottotipo specifico di ISomething. Si consideri una concreta attuazione di ISomething:

public class MySomething : ISomething 
{ 
    public string MyOtherProperty { get; set; } 
} 

Ora, se usiamo il primo, generico, ad esempio, potremmo avere:

MyClass<MySomething> myClass = new MyClass<MySomething>(); 
Console.WriteLine(myClass.MyProperty.MyOtherProperty); 

D'altra parte, se abbiamo usato il secondo esempio, non sarebbe in grado di accedere MyOtherProperty poiché è noto solo per essere un ISomething:

MyClass myClass = new MyClass(); 
Console.WriteLine(myClass.MyProperty.MyOtherProperty); // Won't compile, no property "MyOtherProperty" 

Su una nota diversa, la ragione questi vincoli tipo sono utili è che è possibile fare riferimento a MyProperty (tipo T) e accedere ai membri di ISomething. In altre parole, se ISomething sono state dichiarate come:

public interface ISomething 
{ 
    public string SomeProperty { get; set; } 
} 

allora si potrebbe accedere MyProperty.SomeProperty. Se hai omesso lo where T : ISomething, non saresti in grado di accedere a SomeProperty poiché T sarebbe noto solo per il tipo object.

2

Bene, per cominciare, è possibile chiamare i metodi definiti in ISomething all'interno del codice per il metodo/metodi generici nella classe generica. Se T poteva essere di qualsiasi tipo, questo non sarebbe possibile (anche se potresti sempre fare un casting in runtime).

Quindi consente di imporre vincoli in fase di compilazione su ciò che può essere T e quindi di basarsi su questi vincoli quando si scrive il codice, trasformando gli errori di runtime in errori di compilazione.

7

Tipo Sicurezza. Ad esempio, supponi di creare un contenitore. È possibile passare qualcosa a quel contenitore e recuperarlo nella forma corretta senza dover eseguire alcun cast in un secondo momento parametrizzando il contenitore. Stai semplicemente definendo i vincoli sui tipi di cose che sei disposto a memorizzare nel tuo contenitore.

4

Ecco un esempio della differenza, da solo utilizzando List<>

lista Immagine non sarebbe generico, ma sarebbe solo usare IListElement ovunque usato il generico, invece. Ora immagina di avere un oggetto simile a questo.

class Element : IListElement 
{ 
    public string Something { get; set; } 
} 

ora ho potuto solo fare list.Add(element); e non ci sarebbe una differenza con un vero e proprio List<Element>. Tuttavia, quando ritengo i dati, è una storia diversa, se uso l'elenco che utilizza IListElement, quindi devo trasmettere i miei dati indietro in modo da poter ottenere il Something di esso. Così avrei dovuto fare:

string s = ((Element)list[0]).Something; 

mentre con il generico posso solo fare:

string s = list[0].Something; 

fa risparmiare un sacco di guai, naturalmente va un po 'più di quello, ma penso che si può ottenere l'idea da questo.

1

Sì, è possibile utilizzare ISomething al posto di T, ma che manualmente vicino il tipo generico per una classe ordinaria. Non sarà più un tipo generico. Utilizzando T, si mantiene il tipo aperto in tutti i sottotipi ISomething desiderati. Il riutilizzo del codice senza compromettere la sicurezza del tipo è il vantaggio chiave qui. Ad esempio, se si utilizza una pila di ISomethings, è possibile premere qualsiasi ISomething nello stack ma è necessario eseguire un pop down con il sottotipo effettivo di ISomething per renderlo utile. Downcasting crea un potenziale punto di errore, che non sarà presente in un generico Stack<T> dove T: ISomething

0

Il consumatore della classe ottiene il beneficio di una maggiore sicurezza del tipo, tra gli altri.

class Widget : IPokable { } 

// No generics 
Widget w = (Widget)list[0]; // cast can fail 

// With generics 
Widget w = list[0]; 

Senza farmaci generici, se elenco è stato contenente IPokable oggetti, cast è ancora necessario.

La classe che stai implementando ottiene il vantaggio dell'utilizzo di metodi specifici sull'oggetto generico.

class PokableList<T> where T : IPokable { 
    public T PokeAndGet() { 
     currentObj.Poke(); 
     return currentObj; 
    } 
} 
0

questo video di YouTube in realtà dimostra l'importanza dei vincoli generici https://www.youtube.com/watch?v=GlqBRIgMgho.

Ora di seguito una lunga risposta testuale.

"Generic aiuta a scomporre la logica dal tipo di dati. In modo che abbiamo allegare qualsiasi tipo di dati con qualsiasi logica per alta riutilizzabilità.”

Ma molte volte un po 'di logica può essere collegato a soli tipi di dati specifici.

public class CompareNumeric<UNNKOWDATATYPE> 
{ 
     public bool Compareme(UNNKOWDATATYPE v1, UNNKOWDATATYPE v2) 
     { 
      if (v1 > v2) 
      {return true;} 
      else 
      {return false;} 
     } 
} 

Ad esempio sopra è una semplice classe generica che fa il confronto se un numero è maggiore di un altro numero. Ora il confronto maggiore e minore è molto specifico per i tipi di dati numerici. Questo tipo di confronto non può essere fatto su tipi non numerici come la stringa.

Quindi se alcuni usano le classi con il tipo "int" è perfettamente valido.

CompareNumeric<int> obj = new CompareNumeric<int>(); 
bool boolgreater = obj.Compare(10,20); 

Se qualcuno lo utilizza con "doppio" tipo di dati di nuovo perfettamente valido.

CompareNumeric<double> obj = new CompareNumeric<double>(); 
bool boolgreater = obj.Compare(100.23,20.45); 

Tuttavia, l'utilizzo del tipo di dati stringa con questa logica causerà risultati indesiderati. Quindi vorremmo limitare o porre un vincolo sul tipo di tipi che possono essere associati a una classe generica che si ottiene usando "vincoli generici".

CompareNumeric<string> obj = new CompareNumeric<string>(); 
bool boolgreater = obj.Compare(“interview”,”interviewer”); 

tipo generico può essere limitato specificando il tipo di dati utilizzando il “dove” parola chiave dopo la classe generica, come mostrato nel seguente codice. Ora, se un client tenta di collegare un tipo di dati "stringa" con la classe sottostante non lo consentirà, evitando così risultati indesiderati.

public class CompareNumeric<UNNKOWDATATYPE> where UNNKOWDATATYPE : int, double 
{ 

} 
Problemi correlati