0

So I'm new to generics. But generics seem like a great way to reduce code. here is the scenario. I have a MVC Web API.

http://www.google.com/{controller}/{chartType}/{id} 

NOTE: id is optional

I have several chart types which return similar objects:

  1. HourlyDeviceChart
  2. HourlyUsersCharrt
  3. HourlyAvgProcessingTime etc..

I would like to have just one method that evaluates the chart type parameter and executes the corresponding actions. instead of 8 or 10 methods.

I would be accepting if my design needs some refactoring. I'm open to suggestions. The idea here is to reduce some code. I would hate to have 10 methods exposed in the Web API and then 10 more corresponding methods in another class. Just seems redundant.

As always your suggestions are welcomed!

The method exposed by the API:

IEnumerable<T> GetChart(string chartType)
{
    switch(chartType)
    {
        case "DeviceChart":
        return repository.HourlyDeviceChart();
        break;

        case "UserChart":
         return repository.HourlyUsersChart();
         break;

    }
}

//Then the class that handles all the work would look something like the below

IEnumerable<HourlyDeviceChart> HourlyDeviceChart()
{
    // select appropriate items from the queue
    // populate HourlyDeviceChart object
    // add object to list
    // return HourlyDeviceChart list
}

IEnumerable<UserDeviceChart> HourlyUsersChart()
{
    // do more of the same
}
ekad
  • 14,436
  • 26
  • 44
  • 46
Matthew Knudsen
  • 213
  • 1
  • 7
  • 19
  • possible duplicate of [Best way to do this generic abstract class in c#?](http://stackoverflow.com/questions/9949406/best-way-to-do-this-generic-abstract-class-in-c) – nawfal Jan 16 '14 at 15:49

3 Answers3

1

You can use generics like this:

interface IChart {
    bool IsCharItemNeeded(IChartItem item);
    void AddChartItem(IChartItem item);
}

IEnumerable<T> Charts<T>() where T : new, IChart {
    var res = List<T>();
    foreach (QueueCommand command in MyBigQueue) {
        var chart = new T();
        foreach (IChartItem item in command) {
            if (chart.IsCharItemNeeded(item)) {
                chart.AddChartItem(item);
            }
        }
        res.Add(chart);
    }
    return res;
}

All chart types need to implement the common IChart interface. The where T : new, IChart line provides a constraint that lets you call new T(); for that, all chart classes must also implement a no-argument constructor.

Now you can use your generic code like this:

IEnumerable<UserChart> userCharts = Charts<UserChart>();
IEnumerable<DeviceChart> deviceCharts = Charts<DeviceChart>();
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0

You should be able to do this by using a new interface and applying a constraint to your method GetChart

//Interface with a Type parameter to specify the return type of the method
public interface IChart<T>
{
    IEnumerable<T> HourlyChart();
}

//How to implement the interface
public class HourlyDeviceChart : IChart<HourlyDeviceChart>
{
    public static IEnumerable<HourlyDeviceChart> HourlyChart()
    {
        //Do work
    }
}

//Your new method with a constraint applied
IEnumerable<T> GetChart<T>(string chartType) where T : IChart<T>
{
    return T.HourlyChart();
}
Khan
  • 17,904
  • 5
  • 47
  • 59
0

If both HourlyUserChart and HourlyDeviceChart methods work in same fashion, then dasblinkenlight's answer is good, you can have one generic way of populating them. If you need to populate them differently in two repository methods, may be something like these will do:

1)

interface IHourlyChart {
    IEnumerable<IHourlyChart> Create();
}

class HourlyDeviceChart : IHourlyChart 
{
    public IEnumerable<IHourlyChart> Create()
    {
        return repository.HourlyDeviceChart();
    }
}

class HourlyUsersChart : IHourlyChart 
{
    public IEnumerable<IHourlyChart> Create()
    {
        return repository.HourlyUsersChart();
    }
}

IEnumerable<T> GetChart<T>() where T : IHourlyChart, new()
{
    return (IEnumerable<T>)new T().Create();
}

2) Or make it a tad more strongly typed via generics.

interface IHourlyChart<T> where T : IHourlyChart<T> {
    IEnumerable<T> Create();
}

class HourlyDeviceChart : IHourlyChart<HourlyDeviceChart>
{
    public IEnumerable<HourlyDeviceChart> Create()
    {
        return repository.HourlyDeviceChart();
    }
}

class HourlyUsersChart : IHourlyChart<HourlyUsersChart>
{
    public IEnumerable<HourlyUsersChart> Create()
    {
        return repository.HourlyUsersChart();
    }
}

IEnumerable<T> GetChart<T>() where T : IHourlyChart, new()
{
    return new T().Create();
}

3) Or some reflection, which is still better than your type checking:

IEnumerable<T> GetChart<T>() where T : IHourlyChart, new()
{
    //find necessary method and invoke. may be:
    return repository.GetType()
                     .GetMethods()
                     .Single(x => x.ReturnType == typeof(IEnumerable<T>))
                     .Invoke(repository, new object[0]) as IEnumerable<T>;
}

4) Worst case, do type checking inside your generic method, but check on the type itself, not any magic strings which is very brittle.

IEnumerable<T> GetChart<T>()
{
    if (typeof(T) == typeof(HourlyDeviceChart))
        return (IEnumerable<T>)repository.HourlyDeviceChart();
    else if (typeof(T) == typeof(HourlyUsersChart))
        return (IEnumerable<T>)repository.HourlyUsersChart();

    // throw;
}

Call them all like:

var chartables = GetChart<HourlyUserChart>(); //etc
nawfal
  • 70,104
  • 56
  • 326
  • 368