Как написать общий метод расширения IEnumerable <SelectListItem>

Я довольно новый (нормально, REALLY новый) для дженериков, но мне нравится их идея. Я собираюсь иметь несколько выпадающих списков в представлении, и мне бы хотелось, чтобы общий список объектов и преобразование в список SelectListItems

Что у меня сейчас:

public static IEnumerable<SelectListItem> ToSelectListItems(
     this IEnumerable<SpecificObject> items, long selectedId)
    {
        return
            items.OrderBy(item => item.Name)
            .Select(item =>
                    new SelectListItem
                    {
                        Selected = (item.Id == selectedId),
                        Text = item.Name,
                        Value = item.Id.ToString()
                    });
    }

Проблема в том, что мне нужно будет повторить этот код для каждого раскрывающегося списка, поскольку объекты имеют разные поля, которые представляют свойство Text для SelectListItem

Вот что я хотел бы сделать:

public static IEnumerable<SelectListItem> ToSelectListItem<T>(this IEnumerable<T> items, string key, string value, int SelectedId) {
    // I really have no idea how to proceed from here :(
}

Ответы

Ответ 1

Хорошо, вы могли бы сделать что-то вроде этого:

public static IEnumerable<SelectListItem> ToSelectListItems(
     this IEnumerable<T> items, 
     Func<T,string> nameSelector, 
     Func<T,string> valueSelector, 
     Func<T,bool> selected)
{
     return items.OrderBy(item => nameSelector(item))
            .Select(item =>
                    new SelectListItem
                    {
                        Selected = selected(item),
                        Text = nameSelector(item),
                        Value = valueSelector(item)
                    });
}

Ответ 2

Вы можете передать делегатам возможность выполнять сравнения и поиск свойств. Что-то вроде этого:

public static IEnumerable<SelectListItem> ToSelectListItem<T>(this IEnumerable<T> items, 
    int selectedId, Func<T,int> getId, Func<T, string> getName, 
    Func<T, string> getText, Func<T, string> getValue)
{
    return
        items.OrderBy(item => getName(item))
        .Select(item =>
                new SelectListItem
                {
                    Selected = (getId(item) == selectedId),
                    Text = getText(item),
                    Value = getValue(item)
                });
}

Затем вы будете использовать его так:

var selected = specificObjects.ToSelectListItem(10, s => s.Id, s=> s.Name, s => s.Name, s => s.Id.ToString());

Ответ 3

Для того, чтобы это работало как написано, вашему типу T потребуется реализовать некоторый интерфейс, который предоставляет свойства Name и Id:

 public interface ISelectable
 {
     string Name { get; }
     int Id { get; }
 }

С помощью этого можно сделать:

public static IEnumerable<SelectListItem> ToSelectListItems<T>(this IEnumerable<T> items, long selectedId) 
     where T : ISelectable
{
    return
        items.OrderBy(item => item.Name)
        .Select(item =>
                new SelectListItem
                {
                    Selected = (item.Id == selectedId),
                    Text = item.Name,
                    Value = item.Id.ToString()
                });
}

Это необходимо для использования свойств Name и Id в вашем методе расширения... Вместо этого вы можете предоставить различные способы их получения (то есть: передача делегатов), но это может или может не увеличивайте стоимость обслуживания в вашем сценарии.