Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Generics Refactoring

I am trying to refactor a piece of code which seems easily refactorable but is proving difficult. There are two method which seem very similar and I feel should be refactored:-

public class MyClass
{
    private void AddBasicData(Receiver receiver)
    {
        var aHelper = new AHelper();
        var bHelper = new BHelper();
        var cHelper = new CHelper();
        receiver.ObjA = aHelper.GetBasic();
        receiver.ObjB = bHelper.GetBasic();
        receiver.ObjC = cHelper.GetBasic();
    }

    private void AddExistingData(Receiver receiver)
    {
        var aHelper = new AHelper();
        var bHelper = new BHelper();
        var cHelper = new CHelper();
        receiver.ObjA = aHelper.GetExisting();
        receiver.ObjB = bHelper.GetExisting();
        receiver.ObjC = cHelper.GetExisting();
    }
}

The reference code for this class is here...

public class AHelper : Helper<A> 
{
}

public class BHelper : Helper<B> 
{
}

public class CHelper : Helper<C> 
{
}

public class Helper<T> : IHelper<T> where T : IMyObj
{
    public T GetBasic()
    {
       ...
    }

    public T GetExisting()
    {
       ...
    } 
}

public interface IHelper<T>
{
    T GetBasic();
    T GetExisting();
}

public class A : IMyObj {}
public class B : IMyObj {}
public class C : IMyObj {}

public interface IMyObj {}

public class Receiver
{
    public A ObjA { get; set; }
    public B ObjB { get; set; }
    public C ObjC { get; set; }
}

My first attempt was to refactor like this...

public class MyClass
{
    private void AddBasicData(Receiver receiver)
    {
        Func<Helper<IMyObj>, IMyObj> func = x => x.GetBasic();
        AddData(receiver, func);
    }

    private void AddExistingData(Receiver receiver)
    {
        Func<Helper<IMyObj>, IMyObj> func = x => x.GetExisting();
        AddData(receiver, func);
    }

    private void AddData(Receiver receiver, Func<Helper<IMyObj>, IMyObj> func)
    {
        var aHelper = new AHelper();
        var bHelper = new BHelper();
        var cHelper = new CHelper();
        receiver.ObjA = func(aHelper);
        receiver.ObjB = func(bHelper);
        receiver.ObjC = func(cHelper);
    }
}

The problem with this is objects like new AHelper() is not assignable to Helper<IMyObj> :-(

Can anyone see how this could be nicely refactored?

Thanks in advance

Russell

like image 677
Russell Giddings Avatar asked Aug 07 '09 10:08

Russell Giddings


1 Answers

Try using a templated function. It should infer the type based on the type of parameter you pass, so you shouldn't need to explicitly specify the type in the AddData call.

public class MyClass
{

  private void AddData<T>(Receiver receiver, Func<Helper<T>, T> func)
  {
     var aHelper = new AHelper();
     var bHelper = new BHelper();
     var cHelper = new CHelper();
     receiver.ObjA = func(aHelper);
     receiver.ObjB = func(bHelper);
     receiver.ObjC = func(cHelper);
  }
}

Attempt #2: Tricky problem I think you need a more generic IHelper interface. Would something like this help?

public interface IHelper
{
   IMyObj GetBasic();
   IMyObj GetExisting();
}

public interface IHelper<T> : IHelper
{
   T GetBasic();
   T GetExisting();
}

You'll have to work out the name conflict between the derived interface and the base interface, but I'm not sure exactly how you'd want to do that, and I'm running out of time, so I'll leave that as it for the moment.

Attempt #3 (I'm determined to get this!): Would this be cheating?

  public enum RetrievalMethod
  {
     Basic,
     Existing
  }
  public class Helper<T> : IHelper<T> where T : IMyObj
  {

     public T Get(RetrievalMethod rm)
     {
        switch(rm)
        {
           case RetrievalMethod.Basic:
              return GetBasic();
           case RetrievalMethod.Existing:
              return GetExisting();
        }
     }
...
  }
...
     private void AddData(Receiver receiver, RetrievalMethod rm)
     {
        var aHelper = new AHelper();
        var bHelper = new BHelper();
        var cHelper = new CHelper();
        receiver.ObjA = aHelper.Get(rm);
        receiver.ObjB = bHelper.Get(rm);
        receiver.ObjC = cHelper.Get(rm);
     }
like image 85
BlueMonkMN Avatar answered Nov 08 '22 07:11

BlueMonkMN