Skip to main content
GameDev.net gamedev.net
🔒 Locked

[C#] new() constraint. Is it possible to add parameters?

Started by Spa8nky Jul 23, 2010 at 3:25 PM 12 replies 14.7k views
Original Post
Spa8nky
Spa8nky
If I use the new() constraint on a generic T:

public class Pool<T> where T : class, new()


Is it possible to have a parameter passed into the constructor instead of the default parameterless constructor?
jpetrie
jpetrie
No. The implementation of the constraint mechanism in the CLR metadata essentially uses a bit to indicate that the new-constraint exists.

You can usually achieve what you are probably trying to achieve via the use of interfaces providing factory or allocator methods.
dadooGames
dadooGames
Nope, there is no ability to do that, though it would be useful. Interfaces would be an option, except they don't allow you specify constructor arguments. Having a base class would be the only way to ensure you can call a constructor of a particular type, and that isn't always possible.
www.dadoogames.com
Spa8nky
Spa8nky
Quote:

You can usually achieve what you are probably trying to achieve via the use of interfaces providing factory or allocator methods.


When using interfaces I have the problem of class conversion and lists.

For some reason Bar:

class Bar : IPool


cannot be converted to IPool for T

public class ResourcePool<T> where T : class, IPool


when using a list:

List<ResourcePool<IPool>> list = new List<ResourcePool<IPool>>();list.Add(ResourcePool<Bar>);
jpetrie
jpetrie
You want to use the interface to provide a means of allocating for the pool, i.e., the interface is the allocator and provided to the pool; its not the pool that uses an interface.
Spa8nky
Spa8nky
Could you please explain how?

I don't use IPool with the Pool then I can't use IPool specific properties inside the Pool, which is necessary for the pool to work.
darookie
darookie
I think (correct me if I'm wrong) I see where your problem is.
Generic collections in C# don't work that way, e.g. ResourcePool is different from ResourcePool, which is why you cannot add ResourcePool to the list (or even cast to ResourcePool for that matter).

You need to redesign your class and don't use generics. Try using an interface that exposes IPool as a property, e.g.
interface IPool{}interface IPoolSupport{    IPool Pool { get; }}interface IResourcePool : IPoolSupport{}class ResourcePool<T> : IResourcePool where T : class, IPool, new(){    public ResourcePool()    {        Pool = new T();    }       public IPool Pool { get; private set; }}class Bar : IPool{}var list = new List<IResourcePool>();list.Add(new ResourcePool<Bar>());

This way you can still access IPool from outside of RessourcePool by simply using the getter:
var resPool = list[0];var pool = resPool.Pool;

The missing parameter constraint with new() is makes perfect sense, btw. since the actual implementation of the constructor must be of no concern for classes that implement the interface.
jpetrie
jpetrie
Quote:

Could you please explain how?

Sure thing. What you're wanting to do with a parameterized new constraint is provide a specific signature that must be used to create objects. You can't do that, as we've discussed, with the constraint. But there are other ways to mandate interfaces: interface constraints.

So you can have a factory interface that produces objects of type T:
interface Factory<T> {  T Create( /* any parameters you like */ );


Then your pool can take one of those as a parameter:
class Pool<T> {  public Pool( Factory<T> factory ) {    ...  }}


Then you can use that factory's Create() method to create new instances of T with the appropriate parameters.
NickGravelyn
NickGravelyn
What I did for my pool class was simply allow a delegate to be provided that implemented the creation of the objects. In that way I didn't need the pool to know how to allocate the objects at all (it will fallback to a parameterless constructor if no delegate is given). I personally think this is the easiest solution to what you're trying to do.

public class Pool<T> where T : class{   private Func<T> creation;   public Pool(Func<T> creation)   {      this.creation = creation;   }   public T GetNew()   {      // example; probably not a useful pool if this were how it worked      return creation();   }}


So in this case I could pass any old function I wanted:

Pool<MyObj> pool = new Pool<MyObj>(CreateMyObj);private MyObj CreateMyObj(){   return new MyObj(1, 2, true, "Something");}
Spa8nky
Spa8nky
Quote:
Original post by NickGravelyn
What I did for my pool class was simply allow a delegate to be provided that implemented the creation of the objects. In that way I didn't need the pool to know how to allocate the objects at all (it will fallback to a parameterless constructor if no delegate is given). I personally think this is the easiest solution to what you're trying to do.


I've used delegate instead of Func in order to pass parameters such as Game1 game. How could I create a list of your type of pool when they are class specific?

Quote:
Original post by darookie
I think (correct me if I'm wrong) I see where your problem is.
Generic collections in C# don't work that way, e.g. ResourcePool is different from ResourcePool, which is why you cannot add ResourcePool to the list (or even cast to ResourcePool for that matter)


Using your method gives me the following error:

The type 'IResourcePool' cannot be used as type parameter 'T' in the generic type or method 'ResourcePool<T>'. There is no implicit reference conversion from 'Cthonian.IResourcePool' to 'Cthonian.IPool'.


Quote:
Original post by jpetrie
So you can have a factory interface that produces objects of type T:

Then your pool can take one of those as a parameter:

Then you can use that factory's Create() method to create new instances of T with the appropriate parameters.


How can I create a list of pools in this case that will allow adding of a pool using any type of class?
jpetrie
jpetrie
Quote:

How can I create a list of pools in this case that will allow adding of a pool using any type of class?

The same way you'd have to do before, with your original implementation. You'll note that my example (and Nick's as well) do not change the type signature of the Pool class. This does mean, of course, that you still can't create List>, because Class is not a parent of Class (in general).

You have two solutions. You can fill the list with Pools. This would not have worked in your original attempt (event with the new-constraint) because the instances stored in the pool would have been actual System.Object instances, not instances of the real classes you presumably want. However, it will work in the case where you are using the factory interface or delegate approaches, because the factory/delegate can create the correct concrete type (which will be casted or boxed into System.Object in the pool) and that factory/delegate is provided on a per-pool basis, in the constructor.

The other, more common option is to have Pool implement an non-generic interface (IPool) or derive from a nongeneric possibly-abstract base class, if appropriate. Then you can store a list of IPools.

Both methods require you to cast, in one form or another, the object in the pool back to its concrete type when you fetch the object from the pool. In certain specific cases you may be able to alleviate or work around this, but you mainly have this problem because you're trying to store a list of concrete types that are actually unrelated, so you have to store instead a list of their most-common-base class, which is in the general case System.Object.
Spa8nky
Spa8nky
Quote:
Original post by jpetrie
The other, more common option is to have Pool implement an non-generic interface (IPool) or derive from a nongeneric possibly-abstract base class, if appropriate. Then you can store a list of IPools.


Could you please explain how as I thought I had tried this and it didn't work?

Would I also be able to gain access to the IsValid IPool property using this method?
jpetrie
jpetrie
Quote:

Could you please explain how as I thought I had tried this and it didn't work?

interface IPool {  bool IsValid { get; }}class Pool<T> : IPool {  ...}List<IPool> pools = new List<IPool>();
Spa8nky
Spa8nky
If I use your method then:

        public T GetNewValidObject()        {            if (numberOfInvalidObjects > 0)            {                // Reset the wrap index to oldest object when there are invalid (unused) objects                wrapIndex = objects.Length;                return objects[--numberOfInvalidObjects];            }            else if (isWrappable)            {                if (wrapIndex == 0)                {                    wrapIndex = objects.Length;                }                // Overwrite the oldest object in the pool if there are no free objects                return objects[--wrapIndex];            }            return null;        }


can't return null and

        public void ValidateObjects()        {            for (int i = numberOfInvalidObjects; i < objects.Length; ++i)            {                T o = objects;                if (o.IsValid)                {                    if (i != numberOfInvalidObjects)                    {                        objects = objects[numberOfInvalidObjects];                        objects[numberOfInvalidObjects] = o;                    }                    numberOfInvalidObjects++;                }            }        }


the generic T does not contain a property for IsValid.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.