Evgraf
Evgraf

Reputation: 187

Create list of generics

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

Answers (5)

Thom Smith
Thom Smith

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

Dennis Traub
Dennis Traub

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

Dave
Dave

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

xing
xing

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

D Stanley
D Stanley

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

Related Questions