The best way to make this generic abstract class in C #?

advertisements

I know I'm not doing this right, but I also know there is a way to do this. I'm trying to be as generic and abstract as possible, otherwise my code is going to get real messy. So I'm using strategy pattern here as hell which is the GetAggregateClient() method.

I want to have an abstract class called AbstractAggregate, so that it uses generics. The type that will be used are a series of data classes which are BlogItem, ResourceItem, and AskItem. These data classes all inherit from ListItem.

So that's the background info. The problem here is that I want GetAbstractAggregate() to return an instance of one of the client classes that implements AbstractAggregate with the type of item specified depending on the enum passed in. However, I cannot return an "AbstractAggregate". Compiler won't let me and that makes sense since since the AbstractAggregateFactory class is not a generic.

Does anyone have the best way to do this?

Thanks a lot.

public static class AggregateHelper
{
    public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }
}

public static class AbstractAggregateFactory
{
    public static AbstractAggregate<T> GetAggregateClient(AggregateHelper.AggregateTypes type)
    {
        switch (type)
        {
            case AggregateHelper.AggregateTypes.AskTankTruck:
                return new AskTankTruckAggregate<AskItem>();
            case AggregateHelper.AggregateTypes.TankTruckBlog:
                return new TankTruckBlogAggregate<BlogItem>();
            case AggregateHelper.AggregateTypes.Resources:
                return new ResourcesAggregate<ResourceItem>();
            default:
                throw new AggregateDoesNotExistException();
        }
    }
}

public abstract class AbstractAggregate<T>
{
    public abstract List<T> GetAggregate(Guid[] resourcetypes);

    public abstract T GetSingle(string friendlyname);

}

public class AskTankTruckAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

public class ResourcesAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}


the problem, compiler complains about
...is that you have a method which is 'open' (T) - and you're returning closed generic (with <AskItem> etc.), concrete type really.
i.e. you have to return a ...<T> - ...and you can do that with the method - no matter if the factory is not generic, method can be still.
As for what's the best way to do it,
that's a 'design' question more - and a bit longer story,
I'm not entirely sure what you're trying to achieve (maybe some background story, how much types you might have etc.),

First, you shouldn't (generally speaking, as a best practice or some 'feels good' factor)
inherit your items from ListItem - use some other base class of yours - and if you need a collection use a generic one like List<T> - or create your own IList implementation etc..

Second, thing is that you don't need everything generic. Your base aggregator is generic but custom classes are not, usually, e.g. like this...

abstract class ItemBase  { }
class AskItem : ItemBase { }
class BlogItem : ItemBase { }
class ProvderA : ProviderBase<AskItem>
{
    public override AskItem Get()
    {
        throw new NotImplementedException();
    }
}
class ProvderB : ProviderBase<BlogItem>
{
    public override BlogItem Get()
    {
        throw new NotImplementedException();
    }
}
abstract class ProviderBase<T> where T : ItemBase
{
    public abstract T Get();
}
class Program
{
    static void Main(string[] args)
    {
        ProviderBase<AskItem> provider = GetProvider<AskItem>();
        var item = provider.Get();
    }
    static ProviderBase<T> GetProvider<T>() where T : ItemBase
    {
        if (typeof(T) == typeof(AskItem))
            return (ProviderBase<T>)(object)new ProvderA();
        if (typeof(T) == typeof(BlogItem))
            return (ProviderBase<T>)(object)new ProvderB();
        return null;
    }
}

...that's one implementation.
Basically, not everything 'generic' is always the best way. You have to have enough reasons or 'types' unknown to be possibly used. As with generic you also pay a certain price. Crossing generics to non-generics world is often tricky, and involves reflection if your types can't be inferred by the usage etc.
IMO mistake is making each provider a generic - as it only accepts one type (each concrete), while base is generic. So like the above. Usually generic is also constrained per interface where/where you can.
But then you have a problem as casting back to generic context from an effectively a non-generic class is not straight (also have in mind there are caveats with value types as you have to treat that differently sometimes, often), and vice versa as well.
Hence you need something like cast (object) first.
I'd rather use sort of IOC approach here - e.g. look at the autofac (I'm not associated but I like how it works, nice framework). In that case you'd do something like ...

        container.Register<ProviderBase<AskItem>>(c=> new ProvderA());
        container.Register<ProviderBase<BlogItem>>(c => new ProvderB());

        // and query later...

        ProviderBase<AskItem> provider = container.Resolve<ProviderBase<AskItem>>();

hope this helps some...