2010-11-01 31 views
28

? He leído un excelente artículo sobre MSDN con respecto a los genéricos en C#.¿Por qué usar restricciones genéricas en C#

La pregunta que me vino a la cabeza fue: ¿por qué debería estar usando restricciones genéricas?

Por ejemplo, si uso un código como éste:

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

no puedo cambiar todas las referencias de T en esta clase con ISomething?

¿Cuál es la ventaja de utilizar este enfoque?

+0

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

Respuesta

47

Usted pregunta, "¿no puedo cambiar TODAS las referencias de T en esta clase con ISomething?" Así que creo que quiere decir comparar:

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

Con:

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

En el segundo ejemplo, MyProperty solamente se garantiza que sea una instancia de ISomething. En el primer ejemplo, MyProperty es lo que T es, incluso si ese es un subtipo específico de ISomething. Considere una aplicación concreta de ISomething:

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

Ahora, si usamos la primera, genérica, ejemplo, podríamos tener:

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

Por otro lado, si usamos el segundo ejemplo, no sería capaz de acceder MyOtherProperty ya que sólo es conocido por ser un ISomething:

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

en otro orden de cosas, la razón de que estas restricciones de tipo son útiles es que puede consultar MyProperty (tipo T) y acceder a los miembros de ISomething. En otras palabras, si ISomething fueron declarados como:

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

entonces se podría acceder MyProperty.SomeProperty. Si omite el where T : ISomething, entonces no podrá acceder a SomeProperty ya que T solo se sabe que es del tipo object.

2

Bien, para empezar, puede llamar a los métodos definidos en ISomething dentro del código para el método/métodos genéricos en la clase genérica. Si se permitiera que T fuera de cualquier tipo, esto no sería posible (aunque siempre se puede hacer algo de fundición en tiempo de ejecución).

Por lo tanto, le permite imponer restricciones de tiempo de compilación en lo que T puede ser y, por lo tanto, confiar en estas restricciones cuando escribe el código, convirtiendo los errores de tiempo de ejecución en errores de tiempo de compilación.

7

Tipo Seguridad. Por ejemplo, supongamos que está creando un contenedor. Puede pasar algo a ese contenedor y recuperarlo en la forma correcta sin tener que hacer ningún molde más tarde mediante la parametrización del contenedor. Simplemente está definiendo restricciones sobre los tipos de cosas que está dispuesto a almacenar en su contenedor.

4

He aquí un ejemplo de la diferencia, sólo por el uso List<>

lista Imagen no sería genérica pero sería sólo tiene que utilizar IListElement todas partes se utiliza el genérico en lugar. Ahora imagina que tienes un objeto que es algo como esto.

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

ahora tan sólo pudiera hacer list.Add(element); y no habría una diferencia con un verdadero List<Element>. Sin embargo, cuando retreive de datos es una historia diferente, si uso la lista que utiliza IListElement entonces tienen que desechar mis datos para que pueda obtener el Something fuera de él. Así que tendría que hacer:

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

mientras que con el genérico que sólo puede hacer:

string s = list[0].Something; 

ahorra un montón de problemas, por supuesto que va un poco más lejos que eso, pero creo que se puede saca la idea de esto.

1

sí se puede utilizar en lugar del ISomething T, pero que será manualmente cerca del tipo genérico para una clase ordinaria. Ya no será un tipo genérico. Mediante el uso de T, se mantiene el tipo abierta a tantos subtipos ISomething como desee. La reutilización de código sin poner en peligro la seguridad de tipos es el beneficio clave aquí. Por ejemplo, si se utiliza una pila de ISomethings, se puede empujar cualquier ISomething en la pila, sino un emergente tiene que ocurrir con un abatido al subtipo real de ISomething para que sea útil. Downcasting crea un potencial punto de fallo, que no estará allí en un genérico Stack<T> donde T: ISomething

0

Consumidor de la clase obtiene el beneficio de una mayor seguridad de tipos, entre otros.

class Widget : IPokable { } 

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

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

Sin genéricos, si la lista se contiene IPokable objetos, fundido sigue siendo necesario.

clase está implementando obtiene el beneficio de la utilización de métodos específicos en el objeto genérico.

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

este vídeo de YouTube en realidad pone de manifiesto la importancia de las restricciones genéricas https://www.youtube.com/watch?v=GlqBRIgMgho.

Ahora va a continuación una respuesta textual larga.

"Generic ayuda a desacoplar la lógica del tipo de datos. Así que adjuntar cualquier tipo de datos con cualquier lógica para alta reusabilidad.”

Pero muchas veces de cierta lógica se puede conectar a sólo tipos de datos específicos.

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

Por ejemplo, una clase genérica simple que compara si un número es mayor que otro número. Ahora, la comparación mayor y menor es muy específica para los tipos de datos numéricos. Este tipo de comparación no se puede hacer en tipos no numéricos como cadena.

Así que si algunos usan las clases con "int", el tipo es perfectamente válido.

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

Si alguien lo utiliza con el tipo de datos "doble", vuelve a ser perfectamente válido.

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

Pero el uso de un tipo de datos de cadena con esta lógica dará resultados no deseados. Por lo tanto, nos gustaría restringir o poner una restricción sobre qué tipo de tipos se pueden asociar a una clase genérica, esto se logra mediante el uso de "restricciones genéricas".

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

tipo genérico puede ser restringido mediante la especificación de tipo de datos utilizando el “WHERE” palabra clave después de la clase genérica, como se muestra en el código siguiente. Ahora, si algún cliente intenta adjuntar el tipo de datos "cadena" con la clase siguiente, no lo permitirá, evitando así resultados indeseables.

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

} 
Cuestiones relacionadas