Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to refactor these 2 similar methods into one?

I've seen some samples of using 'T' to make a method reuseable for generic collections of different classes, but I've never really gotten into it or understood the samples.

I wonder if it would be possible to put the 2 methods below into one and what the downsides of doing this would be (performance-wise).

Anyone?

        [NonAction]
        public List<SelectListItem> ToSelectList(IEnumerable<Department> departments, string defaultOption)
        {
            var items = departments.Select(d => new SelectListItem() { Text = d.Code + " - " + d.Description, Value = d.Id.ToString() }).ToList();
            items.Insert(0, new SelectListItem() { Text = defaultOption, Value = "-1" });
            return items;
        }

        [NonAction]
        public List<SelectListItem> ToSelectList(IEnumerable<Function> functions, string defaultOption)
        {
            var items = functions.Select(f => new SelectListItem() { Text = f.Description, Value = f.Id.ToString() }).ToList();
            items.Insert(0, new SelectListItem() { Text = defaultOption, Value = "-1" });
            return items;
        }

SOLUTION

The solution that I used:

usage

var departmentItems = departments.ToSelectList(d => d.Code + " - " + d.Description, d => d.Id.ToString(), " - ");
var functionItems = customerFunctions.ToSelectList(f => f.Description, f => f.Id.ToString(), " - ");

with

 public static class MCVExtentions
    {
        public static List<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, string> text, Func<T, string> value, string defaultOption)
        {
            var items = enumerable.Select(f => new SelectListItem() { Text = text(f), Value = value(f) }).ToList();
            items.Insert(0, new SelectListItem() { Text = defaultOption, Value = "-1" });
            return items;
        }
    }
like image 651
Thomas Stock Avatar asked May 06 '09 08:05

Thomas Stock


3 Answers

The old school way would be to create a common interface for both Department and Function:

interface A
{
int ID{get;}
string Description{get;}
}

You implement Description on Department to return d.Code + " - " + d.Description. and write the function to use this interface instead of concrete classes:

[NonAction]
    public List<SelectListItem> ToSelectList(IEnumerable<A> as, string defaultOption)
    {
        var items = as.Select(a => new SelectListItem() { Text = a.Description, Value = a.Id.ToString() }).ToList();
        items.Insert(0, new SelectListItem() { Text = defaultOption, Value = "-1" });
        return items;
    }

EDIT: Regarding using generics, its not going to help much in this case, because

  • the objects you are passing needs to implement Id and Description
  • you are not returning these objects, so in this respect you don't have to care about type safety of generics
like image 96
Grzenio Avatar answered Sep 29 '22 12:09

Grzenio


Without implementiong a common interface like @Grzenio suggested, you could use a generic method like this:

    public List<SelectListItem> ToSelectList<T>(IEnumerable<T> enumerable, Func<T, string> text, Func<T, string> value, string defaultOption)
    {
        var items = enumerable.Select(f => new SelectListItem() { Text = text(f), Value = value(f) }).ToList();
        items.Insert(0, new SelectListItem() { Text = defaultOption, Value = "-1" });
        return items;
    }

    // use like

    t.ToSelectList(departments, d => d.Code + " - " + d.Description, d => d.Id.ToString(), "default");
    t.ToSelectList(functions, f => f.Description, f => f.Id.ToString(), "default");
like image 28
bruno conde Avatar answered Sep 29 '22 10:09

bruno conde


In fact you can do it with a combination of generics and functions, something along the lines of this (untested may not even compile).

[NonAction]
public List<SelectListItem> ToSelectList<T>(IEnumerable<T> en, 
                                            Function<string, T> text, 
                                            Function<string, T> value, 
                                            string defaultOption)
{
    var items = en.Select(x => new SelectListItem() { Text = text(x) , Value = value(x) }).ToList();
    items.Insert(0, new SelectListItem() { Text = defaultOption, Value = "-1" });
    return items;
}

Then you can dispatch to it with the appropriate lambda functions (or call directly).

[NonAction]
public List<SelectListItem> ToSelectList(IEnumerable<Department> departments, 
                                         string defaultOption)
{
    return ToSelectList<Department>(departments, d =>  d.Code + '-' + d.Description, d => d.Id.ToString(), defaultOption);

}
like image 37
Motti Avatar answered Sep 29 '22 12:09

Motti