Home > OS >  What's the best way to call a function from a generic class with a shared function
What's the best way to call a function from a generic class with a shared function

Time:06-17

I'm trying to figure what would be the best way to call a function off of a generic class.

public T Create<T>(T obj)
{
    switch (obj.GetType().Name)
    {
        case "Bank":
            (new Bank()).Create(obj);
            break;
        case "Bar":
            (new Bar()).Create(obj);
            break;
        case "Beer":
            (new Beer()).Create(obj);
            break;
        default:
            (new Broke()).Create(obj);
            break;
    }

    return obj;
}
I have like 30 classes, so it would be a pretty ugly big switch statement that would be tough to manage and I have to pass in around 100,000 obj. Also it's CRUD so I got to do it 4 times. Reflection is a little on the slow side. So far I've been sitting here at my desk with a giant frowny face trying to think of a good way.

I asked my good friend Alexa, Siri, and Google, but they weren't all that helpful. Normally I'm able to get all the answers I need from one of them...

2 days later

I did a poor job of explaining at the time. But I went ahead with the giant switch and a simplified version looks like this. With the CreateEntity function overloaded accepting each of the different classes.

public Entry Create<T>(T obj, ObjBuilder builder)
{
    Entry entry;
    switch (obj)
    {
        case Profile p:
            entry = builder.CreateEntity(p);
            break;
       case Area a:
            entry = builder.CreateEntity(a);
            break;
        case Credential c:
            entry = builder.CreateEntity(c);
            break;
        case Device d:
            entry = builder.CreateEntity(d);
            break;
    }
    return entry;
}

CodePudding user response:

A simple solution is to have all your entities implement the same interface.

public interface ICreateable<T> {
    T Create();
}

public class Bank : ICreateable<Bank> {
    public Bank Create()
    {
        return new Bank();
    }
    // ...
}

Not really sure what the use case is though.

CodePudding user response:

I'd suggest you look at using a Dictionary<Type, Delegate> to make this work. It might seem complicated, but it gives you the ability to add any number of types at run-time.

Here's the code:

private Dictionary<Type, Delegate> _creators = new Dictionary<Type, Delegate>();

public Entry Create<T>(T obj, ObjBuilder builder) =>
    _creators.ContainsKey(typeof(T))
    ? ((Func<ObjBuilder, T, Entry>)_creators[typeof(T)])(builder, obj)
    : null;

public void Register<T>(Func<ObjBuilder, T, Entry> factory)
{
    _creators[typeof(T)] = factory;
}

Now just register your factories:

Register<Profile>((b, x) => b.CreateEntity(x));
Register<Area>((b, x) => b.CreateEntity(x));
Register<Credential>((b, x) => b.CreateEntity(x));
Register<Device>((b, x) => b.CreateEntity(x));

Easy.

Or you can hard-code the dictionary like this:

private Dictionary<Type, Delegate> _creators = new Dictionary<Type, Delegate>()
{
    { typeof(Profile), (Func<ObjBuilder, Profile, Entry>)((b, x) => b.CreateEntity(x)) },
    { typeof(Area), (Func<ObjBuilder, Area, Entry>)((b, x) => b.CreateEntity(x)) },
    { typeof(Credential), (Func<ObjBuilder, Credential, Entry>)((b, x) => b.CreateEntity(x)) },
    { typeof(Device), (Func<ObjBuilder, Device, Entry>)((b, x) => b.CreateEntity(x)) },
};

Or you can do a combination of both.


Here's the way to do this with the original code you had:

void Main()
{
    Register<Bank>(obj => (new Bank()).Create(obj));
    Register<Bar>(obj => (new Bar()).Create(obj));
    Register<Beer>(obj => (new Beer()).Create(obj));
    Register<Broke>(obj => (new Broke()).Create(obj));
}

private Dictionary<Type, Delegate> _creators = new Dictionary<Type, Delegate>();

public T Create<T>(T obj)
{
    if (_creators.ContainsKey(typeof(T)))
    {
        ((Func<T, T>)_creators[typeof(T)])(obj);
    }
    return obj;
}

public void Register<T>(Action<T> action)
{
    _creators[typeof(T)] = action;
}

CodePudding user response:

Can you try to slice the problem the following way?

public class EntryData
{
int X { get; }
int Y { get; }
}

public interface IEntryData {
   EntryData GetEntryData();
}

public class Profile: IEntryData {
   ...
   public EntryData AsEntry();
}


public class Area: IEntryData {
   ...
   public EntryData AsEntry();
}

...

Option 1:

public Entry Create(object obj, ObjBuilder builder) {
   if(obj is not IEntryData) {...}
   
   IEntryData entryData = obj as IEntryData;

   builder.CreateEntity(entryData);
}

Option 2: 

public Entry Create(IEntryData obj, ObjBuilder builder)  
   => builder.CreateEntity(entryData);
...

class ObjBuilder {
   Entry CreateEntity(IEntryData entryData) {
      // Other things
      var extra = ...

      return new Entry(
         x: entryData.X,
         y: entryData.Y,
         z: extra.Z)
   }
  • Related