Расширение IEnumerable

Я хочу создать расширение IEnumerable<TSource>, которое может преобразовать себя в IEnumerable<SelectListItem>. До сих пор я пытался сделать это следующим образом:

    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;
    }

Это правильный способ сделать это? Если да, то каким образом я рисую значения из соответствующих значений из Func<TSource, TKey>?

Ответы

Ответ 1

Вам просто нужно использовать две функции, которые вы предоставляете в качестве параметров для извлечения текста и значения. Предполагая, что текст и значение являются строками, вам не нужен параметр типа TKey. И нет необходимости создавать список в методе расширения. Блок-итератор с использованием yield return является предпочтительным и как строятся аналогичные методы расширения в 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) };
}

Вы можете использовать его так (вам нужно поставить два лямбда):

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

Однако вы могли бы использовать Enumerable.Select:

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

Ответ 2

Вы изобретаете колесо. Это то, что предназначено Enumerable.Select.

EDIT BY @KeithS: Чтобы ответить на вопрос, если вы хотите этот вывод, вы можете определить способ расширения, который включает Enumerable.Select:

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));
}

Ответ 3

Вы на правильном пути.

Funcs - это методы, хранящиеся в переменных, и вызываются как обычные методы.

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;
}

Если бы я мог порекомендовать, ваши Funcs должны быть Func<TSource, string>, так как текст и значение являются строками в SelectListItem.

Изменить Просто подумал об этом...

Кроме того, вам не нужно создавать внутренний список, но вместо этого можно выполнить возврат доходности. Ниже приведена моя "оптимизированная" версия вашего метода.

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)
        };
    }
}

Вот ссылка на возвращение yeild. Он позволяет вам возвращать ваши результаты в виде элемента в перечислимом, создавая ваше перечисляемое невидимо (для вас).

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

Ответ 4

Мне кажется, что пересечь реку, чтобы получить воду. Почему бы просто не использовать select?

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

если вам действительно нужен метод, вы можете сделать это:

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();  
    }

Ответ 5

Способ LINQ для достижения того, что вы хотите:

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) 
           };
}

Ответ 6

Внутри вашего метода расширения эти два параметра являются просто делегатами, и вы можете запускать их как любую другую функцию:

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

Ответ 7

Другие решения также работают, но я думаю, что один из Martin Liversage - лучший способ сделать это:

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