tags:

views:

334

answers:

6
+1  Q: 

Generic C# method

Hi I have two classes

public class JobDataProvider
{
   public List<Job> Get(int id){
       List<Job> jobs = new List<Job>();
       foreach(up_GetJobResult result in myDataContext.up_GetJob(id))
       {
          jobs.add(new Job(Id = result.Id, name = result.Name));
       }
     return jobs;
   }
}//end class Job

public class PersondataProvider{
    public List<Person> Get(int id){
      List<Person> persons = new List<Persons>();
      foreach(up_GetPersonsResult result in MyDataContext.up_GetPerson(id)){
          persons.add(new Person(Id = result.Id, Name = result.Name, Surname =     result.Surname));
          }
       return persons;
     }
}end class Person

i want to create a generic method, something like this

public List<T> Get<T>(int id)
{
   .. get data and return the list
}
A: 

Here are some possibilities.

IEnumerable<T> Get<T>(int id) { } // returns an IEnumerable containing the data 
                                  // can use yield return inside to eable deferred execution

IList<T> Get<T>(int id) { }       // returns a list

T Get<T>(int id) { }              // returns a single object
Obalix
+1  A: 

Unless there is a common interface or link between what you want to pass as T, and the DataContext which has the stored procedures, this may be hard to do.

However, you might be able to utilize the DynamicObject capability in .NET 4.0 to do this. If your MyDataContext object was a DynamicObject, you could use the convention that your data retrieval method is always up_GetFoo where Foo is also your return Type in concert with TryGetMember to do this.

Nick
+2  A: 

You didn't exactly ask a question, but I'll see if I can guess what you are asking. You want a generic method where you can specify the type you want returned and an ID and have the generic method figure out where to get the data from.

You can create a dictionary with the key being the type (T) and the value being the list you want returned or, perhaps, an instance of an interface that returns the list you want (if the list is dynamic and you don't want to store it in multiple places).

Here is an interface you might try (you will obviously want to add more error handling). Of course this example assumes that you not only have a explicit interface (IDataProvider) but also an implicit interface (IDataProvider.Get must return a properly typed generic List).

public interface IDataProvider
{
  IEnumerable Get(int id);
}

public class JobDataProvider
  : IDataProvider
{

  public List<Job> Get(int id)
  {
    var jobs = new List<Job>();
    // load jobs
    return jobs;
  }

  IEnumerable IDataProvider.Get(int id)
  {
    return Get(id);
  }
}

public class PersonDataProvider
  : IDataProvider
{

  public List<Person> Get(int id)
  {
    var people = new List<Person>();
    // load people
    return people;
  }

  IEnumerable IDataProvider.Get(int id)
  {
    return Get(id);
  }
}

public class ItemDataProvider
{
  private Dictionary<Type, IDataProvider> mProviders = new Dictionary<Type, IDataProvider>();
  public void RegisterProvider(Type type, IDataProvider provider)
  {
    mProviders.Add(type, provider);
  }

  public List<T> Get<T>(int id)
  {
    var data = mProviders[typeof(T)].Get(id);
    return (List<T>)data;
  }
}

public class Job
{
}

public class Person
{
}
Brian
sorry i did not understand completely your code....when i get the list up_GetPersonResult from the database(when /how) i add the items into the list?
GigaPr
I updated my sample code to properly reflect your code snippet.
Brian
A: 

You need an interface

public interface IProvider<TKey, TValue>
{
    IList<TValue> Get(TKey id);
}

Next, your public classes need to implement this interface:

public class JobDataProvider : IProvider<Job, int>
{
    public IList<Job> Get(int id)
    {
    }
}

Now you have your repeatable pattern you were looking for.

Payton Byrd
if i understand him right, he's looking for a generic "DataProvider<T>" so he doesn't have to repeatedly implement the IList<T> Get(int id)-method over and over again.
stmax
A: 

Unless you want to do some super ugly reflection, you MUST add more metadata to your classes. This ideally would come in the form of interface implementation. With that information, you can actually do something with the type parameter of your Get method. This is a basic sample shows how you can then map DataProviders to type parameters. The Registry static class would have to be initialized at some point before being used.

// generic mechanism through which the Get method can lookup a specific provider
interface IDataProvider<T> {
  List<T> Get(int id);
}

// implement the generic interface
public class JobDataProvider : IDataProvider<Job> {
  // existing stuff
}

// implement the generic interface
public class PersondataProvider : IDataProvider<Person> {
  // existing stuff
}

// this class holds the method originally desired    
public static class Helper {
  public static List<T> Get<T>(int id) {
    return Registry.Get<IDataProvider<T>>().Get(id);
  }
}

// this functions as the glue/mapping between type parameters
// and specific, concrete data providers
public static class Registry {
  private static Dictionary<Type, Func<object>> _entries; 

  public static T Get<T>() where T: class {
    return _entries[typeof(T)]() as T;
  }

  public static void Register<T>(Func<T> item) {
    _entries.Add(typeof(T), () => item() as object);
  }
}

Initialization code:

void Init() {
  Registry.Register<IDataProvider<Job>>(() => new JobDataProvider());
  Registry.Register<IDataProvider<Person>>(() => new PersondataProvider());
}

Usage:

var job15 = Helper.Get<Job>(15);
var person2 = Helper.Get<Person>(2);
gWiz
A: 

I'm still hazzy on the nature of the up_GetJobResult class and the up_GetPersonResult classes. Do these share a base class? If the results from MyDataContext always derive from the same base class, that makes it a bit easier:

/// <summary>
/// Interface used on the individual classes that controls 
/// how the class will load it's data.
/// </summary>
public interface ILoadable
{
 /// <summary>
 /// Again, I did not know what type of return value comes back from MyDataContext
 /// If the results are always of the same type or if they derive from the same 
 /// base class, then it makes it simpler. 
 /// </summary>
 void Load( ResultBaseClass result );
}

public class Job : ILoadable
{
 public int Id { get; set; }
 public string Name { get; set; }

 public void Load( ResultBaseClass result )
 {
  var jobResult = result as up_GetJobResult;
  if ( jobResult == null )
   return;
  Id = jobResult.Id;
  Name = jobResult.Name;
 }
}
public static class MyDataContext
{
 public static IList<ResultBaseClass> up_GetPerson( int id );
 public static IList<ResultBaseClass> up_GetJobResult( int id );
}

public class Person : ILoadable
{
 public int Id { get; set; }
 public string Name { get; set; }
 public string Surname { get; set; }

 public void Load( ResultBaseClass result )
 {
  var personResult = result as up_GetPersonResult;
  if ( personResult == null )
   return;
  Id = personResult.Id;
  Name = personResult.Name;
  Surname = personResult.Surname;
 }
}

public abstract class MyProvider<T> where T : ILoadable, new()
{
 /// <summary>
 /// Create a method only visible to derived classes which takes a delegate for the specific
 /// routine that should be called to retrieve data.
 /// </summary>
 protected List<T> Get( int id, Func<int, IList<ResultBaseClass>> getRoutine )
 {
  var result = new List<T>();
  foreach ( var resultInstance in getRoutine( id ) )
  {
   var item = new T();
   item.Load( resultInstance );
   result.Add( item );
  }

  return result;
 }
}

public class JobDataProvider : MyProvider<Job>
{
 public List<Job> Get( int id )
 {
  return base.Get( id, MyDataContext.up_GetJobResult );
 }
}
public class PersonDataProvider : MyProvider<Person>
{
 public List<Person> Get( int id )
 {
  return base.Get( id, MyDataContext.up_GetPerson );
 }
}
Thomas