2009-01-30 13 views
38

¿Por qué no puedes crear un indexador genérico en .NET?¿Por qué no es posible definir indexadores genéricos en .NET?

el siguiente código genera un error del compilador:

public T this<T>[string key] 
    { 
     get { /* Return generic type T. */ } 
    } 

¿Quiere decir esto que no se puede crear un indexador genérico para una colección miembro genérico?

+0

¿Qué sintaxis de uso le gustaría? obj [i] parece que entraría en conflicto con la comparación de obj con T, obj [i] es mejor, pero me resulta difícil de leer. Mi preferencia es obj [ i], pero no por mucho. –

+0

No veo por qué no solo queremos usar un método genérico. Es más limpio, IMO, ya que agregar genéricos a los índices daría lugar a código difícil de leer. – William

Respuesta

1

La única cosa que puedo pensar en esto se puede utilizar es algo a lo largo de estas líneas:

var settings = ConfigurationSection.AppSettings; 
var connectionString = settings<string>["connectionString"]; 
var timeout = settings<int>["timeout"]; 

Pero esto en realidad no comprar nada. Usted sólo ha sustituido ronda parens (como en la configuración (int) [ "tiempo de espera"]) con paréntesis angulares, pero no recibió la seguridad de tipos adicionales como se puede hacer libremente

var timeout = settings<int>["connectionString"]; 

Si usted tiene algo que es fuerte, pero no estáticamente tipado, es posible que desee esperar hasta C# 4.0 con su palabra clave dinámica.

+5

Bueno, en realidad la sintaxis de configuración es preferible. Los moldes de estilo C a menudo te obligan a incluir paréntesis adicionales. –

+0

@John Estoy de acuerdo. Creo que es mejor que crear accesos a métodos como Filed <> y SetFiled <> en DataRow Class, por ejemplo. –

+0

Agregaría seguridad de tipo si se lee 'configuraciones [" foo "]' almacenaría lo último escrito en 'settings [" foo "]', sin importar si algo se había escrito en 'settings [" foo " ] 'antes o después. Lo más importante que hay que tener en cuenta sobre este tipo de diseño es que después de 'settings [" foo "] = someSiameseCat', un intento de leer' settings ["foo"] no vería ese gato, mientras que luego de 'configuración no genérica [ "foo"] = someSiameseCat; '' myAnimal = (Animal) configuración ["SiameseCat"] lo vería. – supercat

9

Puedes; simplemente suelte la parte <T> de su declaración y funcionará bien. es decir,

public T this[string key] 
{ 
    get { /* Return generic type T. */ } 
} 

(Asumiendo que su clase es genérico con un parámetro de tipo llamado T).

+5

Eso no funcionará a menos que defina en el nivel de clase. –

+3

@Igor - por eso dije "Asumiendo que tu clase es genérica con un parámetro de tipo llamado T". La pregunta no indicaba que el parámetro de tipo debería ser diferente del contenedor (¡y en cualquier caso sería un diseño muy inusual!). –

+2

@Greg ... ¿qué tan inusual puede ser? Tome como ejemplo el método genérico Field <> en DataRow. Lo que hicieron fue desempaquetar a través de un método genérico ya que es imposible a través de un indexador genérico. –

15

No sé por qué, pero los indexadores son solo azúcar sintáctico. Escriba un método genérico en su lugar y obtendrá la misma funcionalidad. Por ejemplo:

public T GetItem<T>(string key) 
    { 
     /* Return generic type T. */ 
    } 
17

Las propiedades no pueden ser genéricos en C# 2.0/3.0 por lo tanto, no puede tener un indexador genérico.

+2

¿Tiene un enlace a algo en ese frente? No puedo verlo en el documento de "nuevas características". –

+0

Jon, tienes razón, me retractaré de eso. – Kev

+2

mmmm ... Creo que encontré el enlace que estás buscando. http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=352188 Lo que me parece increíble es que Microsoft dice que no es una característica útil, pero que ya ha implementado algunos extensores de solución (es decir, DataRowExtender) –

23

Aquí hay un lugar donde sería útil. Supongamos que tiene un OptionKey<T> fuertemente tipado para declarar opciones.

public static class DefaultOptions 
{ 
    public static OptionKey<bool> SomeBooleanOption { get; } 
    public static OptionKey<int> SomeIntegerOption { get; } 
} 

Donde las opciones están expuestos a través de la IOptions interfaz:

Código
public interface IOptions 
{ 
    /* since options have a default value that can be returned if nothing's 
    * been set for the key, it'd be nice to use the property instead of the 
    * pair of methods. 
    */ 
    T this<T>[OptionKey<T> key] 
    { 
     get; 
     set; 
    } 

    T GetOptionValue<T>(OptionKey<T> key); 
    void SetOptionValue<T>(OptionKey<T> key, T value); 
} 

podría entonces utilizar el indexador genérico como un buen establecimiento inflexible tienda opciones:

void Foo() 
{ 
    IOptions o = ...; 
    o[DefaultOptions.SomeBooleanOption] = true; 
    int integerValue = o[DefaultOptions.SomeIntegerOption]; 
} 
2

me gusta la capacidad tener un indexador sin entregar una referencia directa al ítem "indexado". Escribí una clase simple "devolver la llamada" paso a paso a continuación ...

R = el tipo devuelto desde el indexador P = el tipo aprobado en el graduador

Todo el indexador realmente hace es pasar a las operaciones el instalador y les permite administrar lo que realmente ocurre y se devuelve.

public class GeneralIndexer<R,P> 
    { 
     // Delegates 
     public delegate R gen_get(P parm); 
     public delegate void gen_set(P parm, R value); 
     public delegate P[] key_get(); 

     // Events 
     public event gen_get GetEvent; 
     public event gen_set SetEvent; 
     public event key_get KeyRequest; 

     public R this[P parm] 
     { 
      get { return GetEvent.Invoke(parm); } 
      set { SetEvent.Invoke(parm, value); } 
     } 

     public P[] Keys 
     { 
      get 
      { 
       return KeyRequest.Invoke(); 
      } 
     } 

    } 

para usarlo en un programa o clase:

private GeneralIndexer<TimeSpan, string> TimeIndex = new GeneralIndexer<TimeSpan,string>(); 

{ 
      TimeIndex.GetEvent += new GeneralIndexer<TimeSpan, string>.gen_get(TimeIndex_GetEvent); 
      TimeIndex.SetEvent += new GeneralIndexer<TimeSpan, string>.gen_set(TimeIndex_SetEvent); 
      TimeIndex.KeyRequest += new GeneralIndexer<TimeSpan, string>.key_get(TimeIndex_KeyRequest); 

} 

obras como un campeón en especial si desea supervisar el acceso a su lista o hacer cualquier operación especial cuando se accede a algo.

Cuestiones relacionadas