One function implementing Generic and non-generic interface - c#

Lets say I have a class, which implements a generic interface
public interface IItem {}
public interface IStuff<out TItem> where TItem : IItem
{
TItem FavoriteItem { get; }
}
public class MyStuff<TItem> : IStuff<TItem> where TItem : IItem
{
public TItem FavoriteItem
{
get { throw new NotImplementedException(); }
}
}
I have also one non-generic interface
public interface IFavoriteItem
{
IItem FavoriteItem { get; }
}
I'd like to make MyStuff class implement this IFavoriteItem interface. Since TItem implements IItem it seems for me, that public TItem FavoriteItem property is implementing IFavoriteItem already.
But compiler doesn't think so, and it wants me to declare a separate IItem IFavoriteItem.FavoriteItem in MyClass. Why is it so? Isn't c# covariance the thing that should play here and solve my problem?
Thanks

The reason for this is that FavoriteItem of IFavoriteItem may not be IItem, where on the IFavoriteItem, it must be an IItem. The only way to solve this is by:
IItem IFavoriteItem.FavoriteItem
{
get { return FavoriteItem; }
}
This will simply shortcut the call to your TItem implementation.
A good example of where this is used quite often is with the implementation of IEnumerable<>. These often look like this:
public class MyEnumerable : IEnumerable<T>
{
public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

Related

Cast custom collection to implemented interfaces

In the following, why Todos1 works, but Todos2 not? How to make it work?
class Program
{
static void Main(string[] args)
{
_todos = new CustomCollection<Todo>();
}
private static CustomCollection<Todo> _todos;
public static IEnumerable<ITodo> Todos1
{
get { return _todos; }
}
public static ICustomCollection<ITodo> Todos2
{
get { return _todos; }
}
public class CustomCollection<T> : Collection<T>, ICustomCollection<T>
{
}
public interface ICustomCollection<T> : IEnumerable<T>
{
}
public interface ITodo
{
}
public class Todo : ITodo
{
public string Description { get; set; }
}
}
This is how variance works; IEnumerable<T> is actually IEnumerable<out T>, meaning it is covariant; this meant that anything that is IEnumerable<Todo> is also IEnumerable<ITodo>, because any Todo is an ITodo.
However, collections / lists / etc are not covariant (or contravariant); so there is no implicit castability here. The reason being:
you have a CustomCollection<Todo>
if that was castable to CustomCollection<ITodo>, you could Add any ITodo
including class SomethingElse : ITodo which is not a Todo
so you'd have a non-Todo in your collection of Todos
The compiler is protecting you!
You should declare your ICustomCollection<T> interface as covariant
public interface ICustomCollection<out T> : IEnumerable<T>
{
}
Otherwise it's invariant and you can cast it only to the same Todo type, which was used for declaration, not the ITodo interface.
IEnumerable<T> already has the covariant generic type parameter T, therefore the first property works as expected.

Inherited Generic Type Unification

For a scenario such as this:
public interface IAnimal
{
}
public interface IGiraffe : IAnimal
{
}
public interface IQuestionableCollection : IEnumerable<IAnimal>
{
void SomeAction();
}
public interface IQuestionableCollection<out T> : IQuestionableCollection, IEnumerable<T>
where T : IAnimal
{
}
public class QuestionableCollection<T> : IQuestionableCollection<T>
where T:IAnimal
{
// Implementation...
}
The complier will generate an error:
'IQuestionableCollection<T>' cannot implement both 'System.Collections.Generic.IEnumerable<IAnimal>' and 'System.Collections.Generic.IEnumerable<T>' because they may unify for some type parameter substitutions
And that makes sense, there is indeed an ambiguity between the two interfaces which C# can't resolve unless it uses the type constraint, which it doesn't per the language spec as #ericlippert explains here.
My question is how should I implement something to the same effect here?
It seems like I should be able to express that the collection is enumerable for the base interface. (I'd like to provide a set of methods that could be utilized without knowing the concrete type, as well as it make some APIs/reflection code cleaner, so I'd like to keep the base collection as non-generic if at all possible. Otherwise, there would be no need for two interfaces.)
The only implementation I can think of that compiles is something like:
public interface IQuestionableCollectionBase
{
void SomeAction();
}
public interface IQuestionableCollection : IQuestionableCollectionBase, IEnumerable<IAnimal>
{
}
public interface IQuestionableCollection<out T> : IQuestionableCollectionBase, IEnumerable<T>
where T : IAnimal
{
}
public class QuestionableCollectionBase<T> : IQuestionableCollection
where T : IAnimal
{
protected List<T> _items = new List<T>();
public void SomeAction() { }
IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); }
IEnumerator<IAnimal> IEnumerable<IAnimal>.GetEnumerator() { return ((IEnumerable<IAnimal>)_items).GetEnumerator(); }
}
public class QuestionableCollection<T> : QuestionableCollectionBase<T>, IQuestionableCollection<T>
where T : IAnimal
{
public IEnumerator<T> GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); }
}
Note that I've had to move any methods I'd like to use on both interfaces to a base method and have two levels of implementation for the class itself - which seems like I'm jumping through enough hoops here that I've got to be missing something...
How should this be implemented?
The simplest workaround is to change the IEnumerables from "is-a" to "has-a", like this:
public interface IAnimal { }
public interface IGiraffe : IAnimal { }
public interface IQuestionableCollection
{
IEnumerable<IAnimal> Animals { get; }
void SomeAction();
}
public interface IQuestionableCollection<out T> : IQuestionableCollection
where T : IAnimal
{
new IEnumerable<T> Animals { get; }
}
public class QuestionableCollection<T> : IQuestionableCollection<T>
where T : IAnimal, new()
{
private readonly List<T> list = new List<T>();
public IEnumerable<T> Animals
{
get { return list; }
}
IEnumerable<IAnimal> IQuestionableCollection.Animals
{
get { return (IEnumerable<IAnimal>)list; }
}
public void SomeAction()
{
list.Add(new T());
}
}
class Giraffe : IGiraffe { }
[TestMethod]
public void test()
{
var c = new QuestionableCollection<Giraffe>();
IQuestionableCollection<Giraffe> i = c;
IQuestionableCollection<IGiraffe> i2 = i;
Assert.AreEqual(0, c.Animals.Count());
Assert.AreEqual(0, i.Animals.Count());
c.SomeAction();
i.SomeAction();
Assert.AreEqual(2, c.Animals.Count());
Assert.AreEqual(2, i.Animals.Count());
}
Note that you can avoid the cast in QuestionableCollection<T> if you add a where T : class constraint.
Changing IQuestionableCollection to a non-generic IEnumerable sorts the compiler issues.
public interface IQuestionableCollection : IEnumerable {...}
I've seen MS use this pattern in their collections, with the non-generic versions using IEnumerable, and the generic ones using IEnumerable<T>.
Alternatively, making the others IEnumerable<IAnimal> also stops the compiler errors, though it means you get IAnimals back instead of T's when enumerating.
You could try this:
public interface IAnimal
{
}
public interface IGiraffe : IAnimal
{
}
public interface IQuestionableCollection<T> : IEnumerable<T> where T : IAnimal
{
void SomeAction();
}
public interface IQuestionableCollection : IQuestionableCollection<IAnimal>
{
}
public class QuestionableCollection<T> : IQuestionableCollection<T>, IEnumerable<T>
where T : IAnimal
{
public void SomeAction() { }
public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
Given the constraints of the language you won't be able to work around the problem of having IQuestionableCollection and IQuestionableCollection both implementing a Generic Interface.
In essence what you are specifying is that IQuestionableCollection implements two possible lists, List and another List. There is no hierarchical relationship here so it impossible to resolve.
That being said you would have to replace IEnumerable with IEnumerable on IQuestionableCollection to provide an inheritance chain to the compiler. In reality there isn't really even a point to declaring the base IQuestionableCollection unless you plan on implementing a vanilla QuestionableCollection
In my updated code I took it out to a consumer of QuestionableCollection to illustrate the enumeration of QuestionableCollection() retains the typing of IGiraffe.
public interface IAnimal {}
public interface IGiraffe : IAnimal { }
public interface IQuestionableCollection : IEnumerable
{
void SomeAction();
}
public interface IQuestionableCollection<out T> : IQuestionableCollection, IEnumerable<T>
where T : IAnimal
{ }
public class QuestionableCollection<T> : IQuestionableCollection<T>
where T : IAnimal
{
private List<T> list = new List<T>();
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void SomeAction()
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
var questionable = new QuestionableCollection<IGiraffe>();
foreach (IGiraffe giraffe in questionable)
{
}
}
}
For the Non Generic Implementation
1 You can always safely upcast
var questionable = new QuestionableCollection();
IEnumerabl<IAnimal> animals = questionable.OfType<IAnimal>();
-or-
2 You can have the NonGeneric QuestionableCollection class implement IEnumerable
public class QuestionableCollection : IQuestionableCollection, IEnumerable<IAnimal>
{
public IEnumerator<IAnimal> GetEnumerator()
{
var l = new List<Giraffe>();
l.Add(new Giraffe());
l.Add(new Giraffe());
return l.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void SomeAction()
{
throw new NotImplementedException();
}
}
Which you then can enumerate without a cast operation.
var questionable = new QuestionableCollection();
foreach (IAnimal giraffe in questionable)
{
var i = giraffe;
}

Specify multiple generic types for implementation

Given the following service structure:
public interface IFoo
{
void Print();
}
public class Foo<T> : IFoo
{
private T _item;
public Foo(T item)
{
_item = item;
}
public void Print()
{
Console.WriteLine(_item);
}
}
Is there a way for me to register the Foo<T> component with multiple types, other than by explicitly enumerating them? This works but I thought there may be a better way:
foreach (var t in myTypes)
{
container.Register(Component.For<IFoo>()
.ImplementedBy(typeof(Foo<>).MakeGenericType(new[] { t })));
}
What you are doing in the foreach type loop is reducing the number of open generic components down to the same number as IFoo; there is a way to wrap this in a clean implementation by using the Castle IGenericImplementationMatchingStrategy interface but this interface only lets you close a generic type with one signature; you cannot close the generic type with mutiple types.
public class YourCustomGenericCloser: IGenericImplementationMatchingStrategy
{
public Type[] GetGenericArguments(ComponentModel model, CreationContext context)
{
if(context.RequestedType == typeof(IFoo))
{
return typeof(TheDefaultTypeToCloseAgainst);
}
return null;
}
}
I think that so far your method is perhaps the simpler way of registering concrete generic types against a basic interface.

Abstract method with strongly typed return type

Consider the following classes :
public abstract class Animal
{
public abstract Animal GiveBirth();
}
public class Monkey : Animal
{
public override Animal GiveBirth()
{
return new Monkey();
}
}
public class Snake : Animal
{
public override Animal GiveBirth()
{
return new Snake();
}
}
//That one doesnt makes sense.
public class WeirdHuman: Animal
{
public override Animal GiveBirth()
{
return new Monkey();
}
}
I'm searching a way to enforce the return types of the overrided GiveBirth method so that it always returns the actual class type, so that no WeirdHuman can give birth to a Monkey.
I feel like the answer is about generic types, but I can't see how I can do that.
Exemple of the expected result :
public abstract class Animal
{
public abstract /*here a way to specify concrete type*/ GiveBirth();
}
public class Monkey : Animal
{
public override Monkey GiveBirth() //Must returns an actual Monkey
{
return new Monkey();
}
}
"Absolutely impossible" may be an answer, if clearly explained.
This is co-variant returns and is not supported by C#. I lament this daily. The best you can hope to do to get around it is to use a generic return type and specify a where condition on the generic type, but this can also cause you to run in to other issues down the road with matching generic parameter requirements.
public abstract class Animal<TBirthType> where TBirthType : Animal<TBirthType>
{
public abstract TBirthType GiveBirth();
}
public class Monkey<TBirthType> : Animal<TBirthType> where TBirthType : Monkey<TBirthType>
{
public override TBirthType GiveBirth()
{
return new Monkey<Monkey>();
}
}
Alternately, if you don't need any further inheritance, you can close the generic.
public class Monkey : Animal<Monkey>
{
public override Monkey GiveBirth()
{
return new Monkey();
}
}
Note that covariance alone is still not enough to ensure that no misbehaving derived type can be formed, but it will allow for the type of the return to be specified as the type being used. There still wouldn't be a way to lock it down from the abstract class though. You could perhaps manage a runtime check via reflection from a method implemented at the base level that would check type at runtime, but this could also be very messy.
As far as I know, there is no clean way to support this purely in a single class hierarchy. Using recurring generic type parameters e.g.
public class Animal<T> where T : Animal<T> { }
may be acceptable if you control the entire hierarchy, and can therefore rule out classes like
public class WierdHuman<Monkey> { }
What you really want is something like Haskell's typeclasses, where you can abstract over the concrete type of the class itself. The closest you can get in C# is to define a surrogate object which implements the required functionality, and then pass that around wherever you require it.
In your case, this means creating an interface for giving birth, and implementing it for each concrete animal type.
Your methods which require this functionality then need an extra parameter for the 'typeclass instance'. These methods can restrict the generic animal type to be the same:
public interface ISpawn<T> where T : Animal
{
public T GiveBirth();
}
public void Populate<T>(T parent, ISpawn<T> spawn) where T : Animal
{
}
You can do something like this, which forces the implementers of Animal<T> to implement an Animal<T> GiveBirth() method which returns the same type as the type parameter, which itself is constrained to be a kind of animal.
That's not quite what you want, but just so you can see:
public abstract class Animal<T> where T: Animal<T>
{
public abstract Animal<T> GiveBirth();
}
public class Monkey: Animal<Monkey>
{
public override Animal<Monkey> GiveBirth()
{
return new Monkey();
}
}
public class Snake: Animal<Snake>
{
public override Animal<Snake> GiveBirth()
{
return new Snake();
}
}
public class WeirdHuman: Animal<WeirdHuman>
{
public override Animal<WeirdHuman> GiveBirth()
{
return new Monkey(); // Won't compile of course.
}
}
If you comment out the public override Animal<Monkey> GiveBirth() methods, you'll see that the compiler complains and says something like:
Error 1 'ConsoleApplication1.Monkey' does not implement inherited abstract member 'ConsoleApplication1.Animal.GiveBirth()'
Unfortunately, you must declare the classes using the SomeKindOfAnimal: Animal<SomeKindOfAnimal> syntax, but maybe this will work for you.
(Also see this thread.)
Alas, this doesn't quite work because it allows you to do this:
public class Monkey: Animal<WeirdHuman>
{
public override Animal<WeirdHuman> GiveBirth()
{
return new WeirdHuman();
}
}
In other words, it constrains the type parameter to be a kind of animal, and it also constrains the return type of GiveBirth() to be the same as the type parameter; but that's all it does. In some cases this is enough, but probably not for your purposes.
Still, perhaps this approach is worth knowing about.
If you have an situation where your base class cannot be generic for various reasons, this method might be useful:
abstract class Animal {
}
interface ICanGiveBirth<T> {
T GiveBirth();
}
static class CanGiveBirthHelper {
public static T GiveBirth<T>(this T v) where T: ICanGiveBirth<T> => v.GiveBirth();
}
class Monkey : Animal, ICanGiveBirth<Monkey> {
public Monkey GiveBirth() {
throw new NotImplementedException();
}
}
class Snake : Animal, ICanGiveBirth<Snake> {
public Snake GiveBirth() {
throw new NotImplementedException();
}
}
If you are unable to add interface to your sub classes, and still unable to add generics to the Base type this method might be useful:
(Unfortunately you cannot make the GiveBirthImpl protected, since the helper class is not allowed to be inside the base class)
abstract class Animal {
public abstract T GiveBirthImpl<T>() where T:Animal;
}
static class CanGiveBirthHelper {
public static T GiveBirth<T>(this T v) where T: Animal => v.GiveBirthImpl<T>();
}
class Monkey : Animal {
public override T GiveBirthImpl<T>() {
throw new NotImplementedException();
}
}
class Snake : Animal {
public override T GiveBirthImpl<T>() {
throw new NotImplementedException();
}
}
In both cases, this will work as expected:
class Tester
{
Monkey TestIt() => new Monkey().GiveBirth();
}

Best way to do this generic abstract class in c#?

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 well, which is the GetAggregateClient() method.
I want to have an abstract class called AbstractAggregate<T>, so that it uses generics. The generic type will be a series of data classes (BlogItem, ResourceItem, and AskItem), which 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<T>. The compiler won't let me, and that makes sense since, since the AbstractAggregateFactory class is not a generic.
Does anyone know 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 the 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, the method still can be.
As for what's the best way to do it, that's more of a design question, and a bit longer story. I'm not entirely sure what you're trying to achieve (maybe some background story, how many types you might have etc.)
First, your items shouldn't (generally speaking, as a best practice or some 'feels good' factor) inherit 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, you don't need to make everything generic. Your base aggregator is generic but custom classes are not, usually. For example:
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, making everything 'generic' is not 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.
In my opinion, it's a mistake making each provider generic (<T>), 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 effectively a non-generic class is not straight (also have in mind there are caveats with value types as you often have to treat that differently), and vice versa as well.
Hence you need something like cast (object) first.
I'd rather use sort of an 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 this:
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.
I'm not sure I understand what you are trying to achieve but perhaps it's something like this
public static class AbstractAggregateFactory
{
public static AbstractAggregate<T> GetAggregateClient<T>()
{
if(T is AskItem) return new AskTankTruckAggregate();
if(T is BlogItem) return new TankTruckBlogAggregate();
if(T is ResourceItem) return new ResourcesAggregate();
}
}
public abstract class AbstractAggregate<T>
{
public abstract List<T> GetAggregate(Guid[] resourcetypes);
public abstract T GetSingle(string friendlyname);
}
public class AskTankTruckAggregate : AbstractAggregate<AskItem>
{
//not implemented yet
}
public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
{
//not implemented yet
}
public class ResourcesAggregate : AbstractAggregate<ResourceItem>
{
//not implemented yet
}
I'm trying to be as generic and abstract as possible, otherwise my code is going to get real messy.
this is a misconception. being generic/abstract can actually complicate an otherwise simple problem. The key to clean code is encapsulation. much different that inheritance or generics.
In this case I think composition would be a better choice, rather than inheritance. with a set of adaptors you could have a common object that each entity could be adpated to. for example:
interface ICommon { ... }
class AskAdaptor: ICommon
{
private readonly Ask ask;
publick AskAdaptor(Ask ask)
{
this.ask = ask;
}
}
class AskAdaptor: ICommon
{
private readonly Blog blog;
publick AskAdaptor(Blog blog)
{
this.blog = blog;
}
}
class AskAdaptor: ICommon
{
private readonly Resource resource;
publick AskAdaptor(Resource resource)
{
this.resource = resource;
}
}
class CommonAggregate
{
public void Add(ICommon common)
{
....
}
}
How about this:
public static class AggregateHelper
{
public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }
}
public class AskItem { }
public class BlogItem { }
public class ResourceItem { }
public static class AbstractAggregateFactory
{
public static AbstractAggregate<T> GetAggregateClient<T>
(AggregateHelper.AggregateTypes type)
{
switch (type)
{
case AggregateHelper.AggregateTypes.AskTankTruck:
return new AskTankTruckAggregate<T>();
case AggregateHelper.AggregateTypes.TankTruckBlog:
return new TankTruckBlogAggregate<T>();
case AggregateHelper.AggregateTypes.Resources:
return new ResourcesAggregate<T>();
default:
throw new ArgumentException();
}
}
}
public abstract class AbstractAggregate<T>
{
public abstract List<T> GetAggregate(Guid[] resourcetypes);
public abstract T GetSingle(string friendlyname);
}
public class AskTankTruckAggregate<T> : AbstractAggregate<T>
{
public override List<T> GetAggregate(Guid[] resourcetypes)
{
throw new NotImplementedException();
}
public override T GetSingle(string friendlyname)
{
Console.WriteLine(friendlyname);
Type whats_t = typeof(T);
return default(T);
}
}
public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
{
//not implemented yet
}
public class ResourcesAggregate<T> : AbstractAggregate<T>
{
//not implemented yet
}
Example:
AbstractAggregate<BlogItem> foo3 =
AbstractAggregateFactory.GetAggregateClient<BlogItem>(AggregateHelper.AggregateTypes.AskTankTruck);
foo3.GetSingle("test");
One thing that is possibly clear is that your design is somewhat flawed. A switch on type is not the best thing to do in a generic method which defeats it's purpose. But what is not clear is what the purpose of your classes are.
Some speculations:
1) Seeing your pair classes AskItem and AskTankTruckAggregate<T> etc I dont think the latter has to be a generic class, it is a very specific class, tightly coupled to AskItem. I would redesign it like
public static class AbstractAggregateFactory
{
public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem
{
//use reflection to find the type that inherits AbstractAggregate<T>
//instantiate the type
//cast to AbstractAggregate<T> and return
}
}
public class AskTankTruckAggregate : AbstractAggregate<AskItem>
{
//not implemented yet
}
public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
{
//not implemented yet
}
public class ResourcesAggregate : AbstractAggregate<ResourceItem>
{
//not implemented yet
}
Call it like:
AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc
2) Another way: delegate the aggregate creation job to your ListItems.
public abstract class ListItem //or interface
{
protected abstract object Create();
}
public class AskItem : ListItem { //implement to return AskTankTruckAggregate
}
public class BlogItem : ListItem { //implement to return TankTruckBlogAggregate
}
public class ResourceItem : ListItem { //implement to return ResourcesAggregate
}
public static class AbstractAggregateFactory
{
public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem, new()
{
return (AbstractAggregate<T>)new T().Create();
}
}
public class AskTankTruckAggregate : AbstractAggregate<AskItem>
{
//not implemented yet
}
public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
{
//not implemented yet
}
public class ResourcesAggregate : AbstractAggregate<ResourceItem>
{
//not implemented yet
}
Call it like:
AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc
3) Or the same, but make it a bit more strongly typed, with the use of generics:
public abstract class ListItem<T> where T : ListItem<T> //or interface
{
protected abstract AbstractAggregate<T> Create();
}
public class AskItem : ListItem<AskItem> { //implement to return AskTankTruckAggregate
}
public class BlogItem : ListItem<BlogItem> { //implement to return TankTruckBlogAggregate
}
public class ResourceItem : ListItem<ResourceItem> { //implement to return ResourcesAggregate
}
public static class AbstractAggregateFactory
{
public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem, new()
{
return new T().Create();
}
}
public class AskTankTruckAggregate : AbstractAggregate<AskItem>
{
//not implemented yet
}
public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
{
//not implemented yet
}
public class ResourcesAggregate : AbstractAggregate<ResourceItem>
{
//not implemented yet
}
Call it like:
AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc
4) Lastly, may be make the return type less generic? Involves switch case, I dont like it.
public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }
public static class AbstractAggregateFactory
{
public static AbstractAggregate GetAggregateClient(AggregateTypes type)
{
switch (type)
{
case AggregateTypes.AskTankTruck:
return new AskTankTruckAggregate<AskItem>();
case AggregateTypes.TankTruckBlog:
return new TankTruckBlogAggregate<BlogItem>();
case AggregateTypes.Resources:
return new ResourcesAggregate<ResourceItem>();
default:
throw new AggregateDoesNotExistException();
}
}
}
public abstract class AbstractAggregate
{
}
public abstract class AbstractAggregate<T> : AbstractAggregate
{
}
//or change the definition to AskTankTruckAggregate : AbstractAggregate<AskItem>
public class AskTankTruckAggregate<T> : AbstractAggregate<T>
{
//not implemented yet
}
//or change the definition to TankTruckBlogAggregate : AbstractAggregate<BlogItem>
public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
{
//not implemented yet
}
//or change the definition to ResourcesAggregate : AbstractAggregate<ResourceItem>
public class ResourcesAggregate<T> : AbstractAggregate<T>
{
//not implemented yet
}
Call it like:
AbstractAggregateFactory.GetAggregateClient(AggregateTypes.AskTankTruck); //etc
Imo, this approach is worse than the reflection approach. Its easy to forget some enum checking in future.
Of all, 3rd looks the best to my eyes, but again without knowing your design goal, its very difficult to predict. Few suggestions:
Your factory name sounds better like AggregateFactory. "Abstract" in it makes it more about implementation.
In case you need an enum to denote type, do not make it nested. Nested public types are harder to call. Take out the wrapping static class (as in my 5th approach).
Rename your base class as Aggregate<T> or AggregateBase<T>. Again "Abstract" in it makes it more about implementation, quite needless.

Categories