0

I have the following class and a factory (omitted unnecessary code). I have 3 separate implementations of the IManageableEntryDao, and a string/type map that gets accessed in the createDao method.

I get the following compilation error: "ManageableEntry.IManageableEntryDao' requires '1' type arguments". What's the best practice in terms of solving this problem? Would I want to somehow determine what is? Or is there an alternative solution?

public interface IManageableEntryDao<T> where T : IManageableEntry {
    T findById(long id);
    T findByName(string name);

    int findUnapprovedCount();
    List<T> findUnapproved(ManageableEntryCriteria criteria);

    long insert(T manageableEntry);
    bool update(T manageableEntry);
    bool delete(T manageableEntry);
}

public class ManageableEntryDaoFactory {
    public IManageableEntryDao createDao(string manageableEntryType) {
            manageableEntryType = manageableEntryType.ToLower();
            Type type = daoTypes[manageableEntryType];
            if (type != null) {
                object dao = Activator.CreateInstance(type);                    
                return dao as IManageableEntryDao;
            }
            throw new NotImplementedException("Failed to find DAO for type: " + manageableEntryType);
        }
}
Adam Levitt
  • 10,316
  • 26
  • 84
  • 145
  • 1
    You need to add the generic argument to `createDao` or `ManageableEntryDaoFactory`, or specify the type parameter explicitly. – vcsjones Jun 08 '12 at 20:11
  • http://stackoverflow.com/questions/731452/create-instance-of-generic-type – asawyer Jun 08 '12 at 20:12

2 Answers2

1

You need to specify a type in the method call. This does mean you can probably avoid the need for the string:

public IManageableEntryDao<T> CreateDao<T>() where T : IManageableEntry
{
        Type manageableEntryType = typeof(T);

        // You'll need to modify daoTypes to be a HashSet<Type> (or List<Type>) of allowable types, or something similar, instead of using a dictionary lookup
        if (daoTypes.Contains(manageableEntryType) {
            object dao = Activator.CreateInstance(type);                    
            return dao as IManageableEntryDao<T>;
        }
        throw new NotImplementedException("Failed to find DAO for type: " + manageableEntryType);
    }
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • How would I actually call this method? Do I need to pass in a generic type into the actual call of CreateDao? ManageableEntryDaoFactory.Instance.CreateDao()? – Adam Levitt Jun 08 '12 at 22:24
  • @AdamLevitt `FooType foo = ManageableEntryDaoFactory.Instance.CreateDao();` You then get concrete types back. – Reed Copsey Jun 08 '12 at 22:27
  • Right, but I'm actually wanting to pass in a type string as opposed to knowing the Dao type. Otherwise I could just create the Dao directly. Does that make sense? – Adam Levitt Jun 08 '12 at 22:33
  • @AdamLevitt How are you going to use the results, then? – Reed Copsey Jun 08 '12 at 22:33
  • The implementation of the DAO interface will make a call that says "findByName()" (which under the hood decides which table to access), and would return IManageableEntry. Does that make sense? – Adam Levitt Jun 08 '12 at 22:38
  • 1
    @AdamLevitt You couldn't get to the properties, though, because they're generic - so they're type specific... – Reed Copsey Jun 08 '12 at 23:07
0

You could:

  • supply a type argument to the IManageableEntryDao<T> by making the CreateDao method (or the ManagableEntryDaoFactory itself) generic. Or,
  • return IManagableEntry interface from the CreateDao method, instead of returning the generic IManagableEntryDao<T>.

EDIT: based on comment

Unfortunately, you cannot return a specific type from the CreateDao method based on an input string. The best you can do is to return a base type or an interface common to all the types in your daoTypes list.

Another idea is to return a non-generic interface and just cast the interface to the specific type in the implemented methods. Here is a little program to illustrate this point:

class Program
{
    static void Main(string[] args)
    {
        var customerEntry = ManageableEntryDaoFactory.CreateDao("customer");
        var orderEntry = ManageableEntryDaoFactory.CreateDao("order");
        customerEntry.Update(new Customer() { Name = "John Doe" });
        orderEntry.Update(new Order() { OrderId = 123 });
        Console.ReadKey();
    }
}

public class Customer
{
    public string Name { get; set; }
}

public class Order
{
    public int OrderId { get; set; }
}

public class CustomerEntry : IManageableEntryDao
{
    public void Update(object objCustomer)
    {
        var customer = objCustomer as Customer;  // now you have a Customer type...
        Console.WriteLine("Updated customer: " + customer.Name);
    }
}

public class OrderEntry : IManageableEntryDao
{
    public void Update(object objOrder)
    {
        var order = objOrder as Order; // now you have an Order type... 
        Console.WriteLine("Updated order: " + order.OrderId);
    }
}

public interface IManageableEntryDao
{
    void Update(object entry);
    // ...other methods, properties, events...
}

public static class ManageableEntryDaoFactory
{
    private static readonly Dictionary<string, Type> daoTypes = new Dictionary<string, Type>() 
    {
        {"customer", typeof(CustomerEntry) }, 
        {"order", typeof(OrderEntry) }
    };

    public static IManageableEntryDao CreateDao(string manageableEntryType)
    {
        manageableEntryType = manageableEntryType.ToLower();
        Type type = daoTypes[manageableEntryType];
        if (type == null)
            throw new NotImplementedException("Failed to find DAO for type: " + manageableEntryType);

        return Activator.CreateInstance(type) as IManageableEntryDao;
    }
}
Eren Ersönmez
  • 38,383
  • 7
  • 71
  • 92
  • This doesn't quite work because i need to actually return the DAO so I can call methods on it. Here's what I did to get it working: public class ManageableEntryDaoFactory where T : IManageableEntryDao { .... public IManageableEntryDao createDao(string manageableEntryType) { – Adam Levitt Jun 08 '12 at 21:27
  • @AdamLevitt unfortunately the type argument cannot be passed in dynamically like that. It has to be known at compile time. – Eren Ersönmez Jun 09 '12 at 06:29