Reputation: 187
I have base class for my entities
public class Entity<T> where T : Entity<T>, new()
{
public XElement ToXElement()
{
}
public static T FromXElement(XElement x)
{
}
}
I have to use this strange construction Entity<T> where T : Entity<T>
, because i want static method FromXElement to be strongly-typed
Also, i have some entities, like that
public class Category : Entity<Category>
{
}
public class Collection : Entity<Collection>
{
}
How can i create a generic list of my entities, using base class?
var list = new List<Entity<?>>();
list.Add(new Category());
list.Add(new Collection());
Upvotes: 5
Views: 285
Reputation: 14086
From the lack of an abstract
marker on Entity
, I assume that To
/FromXElement
use reflection and should work for any subtype of Entity
. I recommend that you structure your classes as follows:
public class Entity
{
public XElement ToXElement() { ... }
protected static T FromXElement<T>(XElement x)
where T : Entity
{
...
}
}
public class Category : Entity
{
public static Category : FromXElement(XElement x)
{
return FromXElement<Category>(x);
}
}
The "boilerplate" is minimal, and it doesn't require that you creatively circumvent the type system. You don't have to worry about the lack of a common base, or about manual conversions. If you like, you can eliminate the boilerplate entirely and just construct your objects directly from Entity
:
public class Entity
{
public XElement ToXElement() { ... }
public static T FromXElement<T>(XElement x)
where T : Entity
{
...
}
}
In essence, what you're doing is implementing a type class, which C# does not directly support. There are a number of ways to work around this lack, but I usually find them to be more trouble than they're worth, especially when it comes to static methods. If C# supported static extension methods, it would be simple, but alas it does not.
Upvotes: 1
Reputation: 51684
Create a marker interface:
public interface IAmAGenericEntity { }
public class Entity<T> where T : IAmAGenericEntity, new()
// ...
public class Category : Entity<T>, IAmAGenericEntity
// ....
var list = new List<IAmAGenericEntity>();
// ...
Upvotes: 1
Reputation: 462
You can solve this problem by adding a non-generic version of the class
class Entity
{
// methods
public T As<T>()
{
if (this is T) return (T)this;
throw new InvalidCastException();
}
}
class Entity<T> : Entity where T : Entity<T>, new()
class Cathegory : Entity<T> {}
and then create the list of the base class:
var list = new List<Entity>()
list.Add(new Cathegory());
Then, if you want to call a "generic specific" operation, you need to call the "As" function or simply cast the object.
Upvotes: 0
Reputation: 447
You can define a non-generic class to be the base class of all entity classes
public class Entity
{
}
and make Entity inherit Entity
public class Entity<T> : Entity where T : Entity<T>, new()
{
}
Now you can create the list of entities as:
var list = new List<Entity>();
Upvotes: 0
Reputation: 152634
You can't with that definition. There is no "common base class" between Category
and Collection
(other than object
, of course).
If there were, say if Entity<T>
were defined as:
public class Entity
{
}
public class Entity<T> : Entity where T : Entity<T>, new()
{
public XElement ToXElement()
{
}
public static T FromXElement(XElement x)
{
}
}
then you could do
var list = new List<Entity>();
list.Add(new Category());
list.Add(new Collection());
But what would that buy you?
Upvotes: 4