2010-07-09 17 views
7

¿Es posible crear un método de extensión que devuelva la instancia que invoca el método de extensión?Métodos de extensión de encadenamiento en C#

Me gustaría tener un método de extensión para cualquier cosa que herede de ICollection<T>, devuelve el objeto. Al igual que jQuery siempre devuelve el objeto jquery.

public static object AddItem<T>(this ICollection<T> collection, T itemToAdd) 
{ 
    collection.Add(itemToAdd); 
    return collection; 
{ 

imagino algo así como el anterior, pero no estoy seguro de cómo volver a la matriz a la "ese" tipo de objeto para el uso de algo como esto:

List<int> myInts = new List<int>().AddItem(5); 

EDIT: Sólo quería para aclarar que esperaba una única solución de restricción genérica.

Respuesta

13

Si necesita devolver el tipo específico, puede utilizar una restricción genérica:

public static TCollection AddItem<TCollection, TElement>(
    this TCollection collection, 
    TElement itemToAdd) 
    where TCollection : ICollection<TElement> 
{ 
    collection.Add(itemToAdd); 
    return collection; 
} 

He probado esto y funciona en VS2010.

Update (en relación con jQuery):

jQuery encadenamiento funciona muy bien porque JavaScript usa tipado dinámico. C# 4.0 soporta dynamic, por lo que se puede hacer esto:

public static dynamic AddItem<T>(this ICollection<T> collection, T itemToAdd) 
{ 
    collection.Add(itemToAdd); 
    return collection; 
} 

Sin embargo, recomiendo la versión genérica limitación, ya que es más de tipo seguro, más eficiente, y permite IntelliSense en el tipo devuelto. En escenarios más complejos, las restricciones genéricas no siempre son capaces de expresar lo que necesita; en esos casos, se puede usar dynamic (aunque no se vinculará a métodos de extensión adicionales, por lo que no funciona bien con el encadenamiento).

+0

Gracias. Hice esto al principio, pero no me gustaron las 2 especificaciones genéricas. PERO, descubrí que dado que el tipo está implícito en TCollection, en realidad no es necesario especificarlo en el código. –

+0

Derecha. El código para usarlo es exactamente lo que tenía en su pregunta; no se necesitan parámetros de tipo explícitos. También actualicé la respuesta con respecto a jQuery. –

4

Si bien no tengo VS abierto a probar esto, algo en este sentido debería funcionar:

public static TCollection AddItem<TCollection, TItem>(TCollection collection, 
                 TItem itemToAdd) 
where TCollection : ICollection<TItem> 
{ 
    collection.Add(itemToAdd); 
    return collection; 
} 
0

sólo devuelve ICollection<T> en lugar de object y todo debería funcionar como si lo encaminó.

+1

No, porque lo está asignando a una variable de tipo 'List '. –

+1

Eso es correcto. Creí equivocadamente que el reparto no funcionaría, pero eso era ReSharper poniendo una línea roja y simplemente tardando mucho tiempo en actualizar después de que reemplacé alguna sintaxis anterior. –

+1

Lo siento, me equivoqué de nuevo, Adam está en lo cierto. –

2

Parece que tiene 2 objetivos en conflicto, y todo se reduce a lo que quiere que sea su método de extensión para volver:

  • La instancia que invoca el método de extensión (la colección)
  • o el artículo que se añadió a la colección

Desde su uso de ejemplo, citó aquí:

List<int> myInts = new List<int>().AddItem(5); 

Hace que parezca que desea devolver la colección.En cualquier caso, que la asignación todavía no funcionará sin una conversión, ya que su método de extensión tendría que tener un tipo de retorno de ICollection, así:

public static ICollection<T> AddItem<T>(this ICollection<T> collection, T itemToAdd) 
{ 
    collection.Add(itemToAdd); 
    return collection; 
} 

Eso le permitiría hacer esto:

List<int> myList = (List<int>) new List<int>().AddItem(5); 

Ahora, si prefiere devolver el objeto que se agregó, todavía no debe tener un tipo de devolución de object. Usted debe tomar ventaja de su parámetro de tipo genérico, y volver T, así:

public static T AddItem<T>(this ICollection<T> collection, T itemToAdd) 
{ 
    collection.Add(itemToAdd); 
    return itemToAdd; 
} 

Sin embargo, si devuelve el elemento que se ha añadido, usted no será capaz de cadena como esta:

List<int> myList = (List<int>) new List<int>().AddItem(5); 

, ya que el tipo de retorno de AddItem(5) es no ICollection, pero es T (int, en este caso). Aún se pueden encadenar sin embargo, justo al lado del valor añadido, así:

List<int> myList = new List<int>(); 
myList.AddItem(5).DoSomethingWithMyInt(); // Not very useful in this case 

Parece que el primer escenario es más útil (volviendo la colección), ya que le sí permite cadena, justo al lado de la primera sentencia de asignación. Aquí está un ejemplo más de que:

List<int> myList = (List<int>) new List<int>().AddItem(1).AddItem(2); 

O, si usted no desea emitir, puede llamar ToList() en ICollection que regresa:

List<int> myList = new List<int>().AddItem(1).AddItem(2).ToList(); 
1

EDIT: Sólo quería Tenga en claro que esperaba una única solución de restricción genérica.

En este caso, usted está de suerte porque las conversiones de tipos de retorno pueden ser covariante, pero no contravariant (es decir, no se puede convertir implícitamente ICollection<T>-List<T>), por lo que sin un tipo de retorno genéricos este no se puede hacer.

¿Qué hay de malo en especificar 2 parámetros de tipo de todos modos? Se pueden inferir por los argumentos que proporciona a la función, por lo que ni siquiera los verá en el código de llamada.

Cuestiones relacionadas