2012-05-29 14 views
15

Quiero hacer una extensión IEnumerable<TSource> que se puede convertir en IEnumerable<SelectListItem>. Hasta ahora he estado tratando de hacerlo de esta manera:Extensión IEnumerable

public static 
     IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(this 
     IEnumerable<TSource> enumerable, Func<TSource, TKey> text, 
             Func<TSource, TKey> value) 
    { 
     List<SelectListItem> selectList = new List<SelectListItem>(); 

     foreach (TSource model in enumerable) 
      selectList.Add(new SelectListItem() { Text = ?, Value = ?}); 

     return selectList; 
    } 

¿Es esta la manera correcta de hacerlo? Si es así, ¿cómo dibujo los valores de los valores apropiados del Func<TSource, TKey>?

+5

También podría considerar olvidar 'selectList' y hacer' foreach (...) yield return new SelectListItem {...} ', que permite la ejecución diferida (que es más LINQ-y). – Rawling

+1

¿por qué no solo usa Select? –

+0

Hay muchos lugares en mi código donde llamaré esto y al mirar la función Seleccionar parece que tendría que escribir más código y luego hacerlo de esta manera. Usando Select I tendría que escribir el código para instanciar un SelectListItem en lugar de solo pasar en el Func <>. Además de usar este método, el código que maneja la transferencia a SelectListItem está encapsulado y almacenado en un área de mi código en lugar de en mi aplicación. –

Respuesta

15

Sólo tiene que utilizar las dos funciones que usted provee como parámetros para extraer el texto y el valor. Suponiendo que tanto el texto como el valor son cadenas, no necesita el parámetro de tipo TKey. Y no hay necesidad de crear una lista en el método de extensión. Se prefiere un bloque de iterador usando yield return y cómo se construyen métodos de extensión similares en LINQ.

public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
    this IEnumerable<TSource> enumerable, 
    Func<TSource, string> text, 
    Func<TSource, string> value) 
{ 
    foreach (TSource model in enumerable) 
    yield return new SelectListItem { Text = text(model), Value = value(model) }; 
} 

Puede utilizar de esta manera (que necesita para abastecer los dos lambdas):

var selectedItems = items.ToSelecListItem(x => ..., x => ...); 

Sin embargo, usted podría también utilizar Enumerable.Select:

var selectedItems = items.Select(x => new SelectListItem { Text = ..., Value = ... }); 
+0

Uso algo similar de vez en cuando, aunque agrego 3 parámetros opcionales (predeterminado a falso y 2 cadenas vacías) y comienzo el método con 'if (includeEmptyOption) yield return new SelectListItem {Text = emptyOptionText, Value = emptyOptionValue};' Parámetros ordenado como se usa para que pueda especificar emptyOptionText y obtener el valor predeterminado emptyOptionValue of "". –

15

Estás en el camino correcto.

Los funcs son métodos almacenados en variables y se invocan como métodos normales.

public static IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(
    this IEnumerable<TSource> enumerable, 
    Func<TSource, TKey> text, 
    Func<TSource, TKey> value) 
{ 
    List<SelectListItem> selectList = new List<SelectListItem>(); 

    foreach (TSource model in enumerable) 
    { 
     selectList.Add(new SelectListItem() 
     { 
      Text = text(model), 
      Value = value(model) 
     }); 
    } 

    return selectList; 
} 

Si Me podría recomendar, sus Funcs deben ser Func<TSource, string> como el texto y el valor son cadenas en el SelectListItem.

Editar acaba de ocurrir esto ...

También usted no tiene que crear una lista de interior, pero se puede hacer una declaración de rendimiento lugar. Debajo está mi versión "optimizada" de su método.

public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
    this IEnumerable<TSource> enumerable, 
    Func<TSource, string> text, 
    Func<TSource, string> value) 
{ 
    foreach (TSource model in enumerable) 
    { 
     yield return new SelectListItem() 
     { 
      Text = text(model), 
      Value = value(model) 
     }; 
    } 
} 

Aquí es la referencia para yeild volver. Le permite devolver los resultados como un elemento en un enumerable, construyendo su enumerable de forma invisible (para usted).

http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

+0

En su recomendación, ¿quiso decir 'Func '? – Richard

+0

Sí. Agregué una edición con código actualizado. –

+0

¿Cómo llamaría algo como esto? Todavía soy nuevo en esto. –

0

Dentro del cuerpo de su método de extensión, estos dos parámetros son sólo los delegados, y se puede ejecutar como cualquier otra función:

 selectList.Add(new SelectListItem() { Text = text(model), Value = value(model)}); 
26

Estás re-inventar la rueda. Es para lo que se diseñó Enumerable.Select.

de edición @KeithS: Para responder a la pregunta, si desea que esta salida, se puede definir un método de extensión Enumerable.Select envoltura:

public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
    this IEnumerable<TSource> enumerable, 
    Func<TSource, string> text, 
    Func<TSource, string> value) 
{ 
    return enumerable.Select(x=>new SelectListItem{Text=text(x), Value=value(x)); 
} 
4

Para mí parece como cruzar un río para obtener agua. ¿Por qué no simplemente usar seleccionar?

enumerable.Select(item => 
        new SelectListItem{ 
          Text = item.SomeProperty, 
          Value item.SomeOtherProperty 
        }).ToList(); 

si realmente quiere un método entonces se podría hacer esto:

public static 
     IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(this 
     IEnumerable<TSource> enumerable, Func<TSource, TKey> text, 
             Func<TSource, TKey> value) 
    { 
     return (from item in enumerable 
       select new SelectListItem{ 
         Text = text(item), 
         Value = value(item) 
       }).ToList(); 
    } 
4

Una forma de LINQ de lograr lo que quiere serían:

public static IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(
    this IEnumerable<TSource> enumerable, 
    Func<TSource, TKey> textSelector, 
    Func<TSource, TKey> valueSelector) 
{ 
    return from model in enumerable 
      select new SelectListItem 
      { 
       Text = textSelector(model), 
       Value = valueSelector(model) 
      }; 
} 
+0

gracias por replicar mi respuesta. Un buen punto no se repite :) (para replicar la funcionalidad, sin embargo debes convertirla en una lista o al menos indicar por qué crees que no es una buena idea) –

+0

Me parece que nuestras respuestas son ligeramente diferentes, se convierte a una lista mientras que Yo no. No es necesario convertir a una lista ya que la declaración de función especifica que el resultado es IEnumerable, al no convertir a una lista un llamante de la función podría componer el resultado con otras operaciones LINQ y por lo tanto cortocircuitar la enumeración completa de la fuente (ej. .Primero, .Single, etc.) –

0

Otras soluciones funcionan tan bien , pero creo que el de Martin Liversage es la mejor manera de hacerlo:

IEnumerable<SelectListItem> selectListItems = items.Select(x => 
    new SelectListItem 
     { 
      Text = x.TextProperty, 
      Value = x.ValueProperty 
     }); 
Cuestiones relacionadas