Search code examples
c#genericsinterfaceabstraction

Get Concrete Implementation of Interface with Generic Parameter


I'm feeling pretty dumb right now. Not sure why I can't solve this. I have a repository interface:

public interface ICollateralItemBaseImplementation<T> where T : CollateralItemBase
{
    int Add(T collateralItem);
    T Get(int collateralID);
}

and many implementations, each implementing one of the 10 models that inherit from CollateralItemBase. For example:

internal sealed class CollateralItemCertifiedDepositRepository : ServiceBaseRepository, ICollateralItemBaseImplementation<CollateralItemCertifiedDeposit>
{
   int Add(CollateralItemCertifiedDeposit collateralItem) { /*...*/ }
   CollateralItemCertifiedDeposit Get(int collateralID)  { /*...*/ }
}  

Now i just need to switch on the incoming CollateralItemBase type to get the repository I need:

private ICollateralItemBaseImplementation<???> GetRepository(CollateralItemBase item) 
{
    switch (item.GetType().Name)
    {
            case "CollateralItemCertifiedDeposit": return new CollateralItemCertifiedDepositRepository();
            //...
    }

I just need to figure out what to return from this method, for other methods to act on whichever repository I return. How do I refactor this to get it working? I'm pretty sure I have a covariance/contravariance problem. Again, I'm feeling pretty dumb, just drawing a blank.

Thanks.


Solution

  • You could do it in two stages. Add a non-generic base interface to ICollateralItemBaseImplementation then cast to the generic version.

    public interface ICollateralItemBaseImplementation
    {
    }
    
    public interface ICollateralItemBaseImplementation<T> : ICollateralItemBaseImplementation
        where T : CollateralItemBase
    {
        int Add(T collateralItem);
        T Get(int collateralID);
    }
    
    public static class RepositoryFactory
    {
        public static ICollateralItemBaseImplementation<T> GetRepository<T>(T item)
            where T : CollateralItemBase
        {
            return (ICollateralItemBaseImplementation<T>)GetRepositoryImpl(item);
        }
    
        private static ICollateralItemBaseImplementation GetRepositoryImpl<T>(T item)
                where T : CollateralItemBase
        {
            switch (item.GetType().Name)
            {
                case "CollateralItemCertifiedDeposit":
                    return new CollateralItemCertifiedDepositRepository();
            }
            return null;
        }
    }
    
    internal static class Program
    {
    
        static void Main()
        {
            var repo = RepositoryFactory.GetRepository(new CollateralItemCertifiedDeposit());
            Debug.Assert(repo is CollateralItemCertifiedDepositRepository);
    
        }
    }