0

I have an interface

public interface ITask
{
  void MethodA();
}

And two classes implementing the ITask

public class TaskA: ITask
{
  public void MethodA();
}

and

public class TaskB : TaskA,ITask
{
}

Now based on some condition I wish to create an instance of either TaskA or TaskB. How can I use generics here and how to initialize the object

public void MethodB(int a)
{
  ITask task;
  if(a=1)
  {
    // use TaskA
  }
  else
  {
    //use TaskB
   }
}
SWeko
  • 30,434
  • 10
  • 71
  • 106
xaria
  • 842
  • 5
  • 24
  • 47

4 Answers4

1

If you mean that you may have a generic type or method parameter, you could do this:

public class A<T> where T : ITask, new()
{
    public void Some() 
    {
         T instanceOfITask = new T();
    }
}

...or:

public class A
{
    public void Some<T>() where T : ITask, new()
    {
         T instanceOfITask = new T();
    }
}

Generic constraints let you specify that T must implement ITask and must have a public parameterless constructor.

UPDATE

As OP has edited the question maybe my current answer got obsolete.

By the way, as I don't know your actual requirements, I could argue that you can still go with this solution.

Instead of doing the if some condition inside the method that should handle instances of ITask, you could do in the caller and take advantage of generic constraints again to avoid reflection and its performance penalties.

At the end of the day, this is using the abstract factory pattern:

// The abstract factory!
public static class TaskFactory
{
    public static T Create<T>() where T : ITask, new()
    {
         T instanceOfITask = new T();
         // more stuff like initializing default values for the newly-created specific task

         return instanceOfITask;
    }
}

Later, somewhere:

ITask task = null;

// Depending on the condition, you invoke the factory method with different implementation of ITask
if([some condition])
{
   task = TaskFactory.Create<MySpecificTaskImplementationA>();
}
else
{
   task = TaskFactory.Create<MySpecificTaskImplementationB>();
}
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • +1 forgot about `new()` constraint. Anyway, IoC containers are much-much more plausible here – Ilya Ivanov Feb 12 '13 at 08:45
  • @IlyaIvanov Yeah! OP was asking how generics may help in this problem. It's true that, in a real-world scenario, this would be solved using IoC (I do this way in my projects). – Matías Fidemraizer Feb 12 '13 at 08:53
0

suppose in your Itask we have

public interface Itask
    {
        IQueryable<T> TaskA();
        Iqueryable<T> TaskB();
    }

then to create instance..

public class Tasks:Itask
    {
        CategoryDataEntities db = new CategoryDataEntities();

        public IQueryable<Category> CategoryList()
        {
            IQueryable<Category> categoryLiist = db.Categories.AsQueryable();

            return categoryLiist;
        }
    }
Neel
  • 11,625
  • 3
  • 43
  • 61
0

I question whether generics are needed. You already have an interface contract, which is good. In which case, you simply need a factory class or method which builds the instances based on some input condition.

Using Activator.CreateInstance() assumes that an implementor of ITask could be in a dynamically-loaded assembly.

class Factory 
{
    public ITask Build(string input)
    {
        switch(input) {
            case "A":
                return (ITask)Activator.CreateInstance(typeof(TaskA));
            case "B":
                return (ITask)Activator.CreateInstance(typeof(TaskB));
            default;
                throw new Exception();
        }
    }
}
Matt
  • 1,648
  • 12
  • 22
  • 1
    If the type (`TaskA`, `TaskB`) is know at compile time, why use `Activator.CreateInstance` instead of just `new TaskA()` and `new TaskB()`? – sloth Feb 12 '13 at 08:41
  • Yes that's a good point. I assume that the questioner's use of "dynamically" indicates that he wishes to load types from a dynamically-loaded assembly, but if not then just new TaskA()/new TaskB() would be fine. – Matt Feb 12 '13 at 08:43
0

It doesn't seem that you need generics at all, it seems like you need a factory. Here is a simple and naive implementation:

public static class TaskFactory
{
   public static ITask CreateTask(TaskType taskType)
   {
      switch (taskType)
        cast TaskType.TaskA:
          return new TaskA();
        cast TaskType.TaskB:
          return new TaskB();
        default:
          throw new InvalidArgumentException("unknown task type");
   }
}

where TaskType is an enum defined like:

public enum TaskType
{
   TaskA,
   TaskB,
}

You can use this in code like

ITask myTast = TaskFactory.CreateTask(TaskType.TaskA);

So, as you can see, generics are not mandatory in this case. However, if you do have paremeterless constructors, and the types are known at compile time, you can indeed use generics to provide the CreateTask method with a type parameter, like this:

public static T CreateTask<T>() where T : ITask, new()
{
   return new T();
}

that could be used like:

ITask myTast = TaskFactory.CreateTask<TaksA>();

both implementation can live side by side, and there are scenarios where both are usefull.

SWeko
  • 30,434
  • 10
  • 71
  • 106
  • Good example, but what about maintainability issues? – Ilya Ivanov Feb 12 '13 at 08:44
  • This is the "simple and naive" implementation, just to show the form and the power of the pattern. – SWeko Feb 12 '13 at 08:45
  • 1
    Yes, ok, I understand. While simplicity is very desirable, implementation with generics is a little bit more plausible here, because of necessity to maintain `enum` and `switch` when adding net types – Ilya Ivanov Feb 12 '13 at 08:48