tags:

views:

361

answers:

5

Hello.

I have the following C# test code:

  class MyItem
  {
    MyItem( int a ) {}
  }

  class MyContainer< T >
    where T : MyItem, new()
  {
    public void CreateItem()
    {
      T oItem = new T( 10 );
    }
  }

Visual Studio can't compile it, the error is at line where 'new' is used:

'T': cannot provide arguments when creating an instance of a variable type

Is it possible in C# to create an object of generic type with non-parameterless constructor? It's no problem to do such thing in C++ templates, so i'm very curious why i can't do same thing in C#. Maybe some additional 'where' is required or syntax is different?

+8  A: 

There's no such generic constraint, so it's not possible directly (this is a CLR limitation). If you want this, you have to provide a factory class (which has a parameterless constructor), and pass it as a second generic type parameter.

Pavel Minaev
+17  A: 

C#, and VB.Net for that matter, do not support the notion of constraining a generic to have a constructor with specific parameters. It only supports constraining to have an empty constructor.

One work around is to have the caller pass in a factory lambda to create the value. For instance

public void CreateItem(Func<int,T> del) {
  T oItem = del(10);
}

Call site

CreateItem(x => new SomeClass(x));
JaredPar
Very clever approach!
Greg
+5  A: 

It can be done with reflection:

public void CreateItem()
{
  int constructorparm1 = 10;
  T oItem = Activator.CreateInstance(typeof(T), constructorparm1) as T;
}

But there is no generic constraint to ensure that T implements the desired constructor, so I wouldn't advise doing this unless you are careful to declare that constructor in every type that implements the interface.

Greg
As I recall, the `new()` constraint translates to an `Activator.CreateInstance()` call.
Steve Guidi
+1  A: 

One pattern I use is to have the constrained class implement an interface which defines an Init method with the appropriate signature:

interface IMyItem
{
    void Init(int a);
}

class MyItem : IMyItem
{
    MyItem() {}
    void Init(int a) { }
}    

class MyContainer< T >
    where T : MyItem, IMyItem, new()
{
    public void CreateItem()
    {
     T oItem = new T();
     oItem.Init( 10 );
    }
}
Daniel Schaffer
Generally a bad idea, because now you can have created but uninitialized objects.
Pavel Minaev
+1  A: 

IMO, the best approach here is an initialize method, i.e.

interface ISomeInterface {
    void Init(int i);
}
class Foo : ISomeInterface {
    void ISomeInterface.Init(int i) { /* ... */ }
}
static class Program {
    static T Create<T>(int i) where T : class, ISomeInterface, new() {
        T t = new T();
        t.Init(i);
        return t;
    }
    static void Main() {
        Foo foo = Create<Foo>(123);
    }
}

However, you can do what you want with Expression (but without compile-time support):

using System;
using System.Linq.Expressions;
class Foo {
    public Foo(int i) { /* ... */ }
}
static class Program {
    static T Create<T>(int i) {
        return CtorCache<T>.Create(i);
    }
    static class CtorCache<T> {
        static Func<int, T> ctor;
        public static T Create(int i) {
            if (ctor == null) ctor = CreateCtor();
            return ctor(i);
        }
        static Func<int, T> CreateCtor() {
            var param = Expression.Parameter(typeof(int), "i");
            var ci = typeof(T).GetConstructor(new[] {typeof(int)});
            if(ci == null) throw new InvalidOperationException("No such ctor");
            var body = Expression.New(ci, param);
            return Expression.Lambda<Func<int, T>>(body, param).Compile();
        }
    }
    static void Main() {
        Foo foo = Create<Foo>(123);
    }
}

Note that this caches and reuses the delegate for performance.

Marc Gravell
Does this significantly differ from using `Activator.CreateInstance()`? If I understand correctly, neither has compile-time support, and both throw a run-time error if the expected constructor doesn't exist.
Greg
@Greg - yes, if you are using it lots (for example, a factory): once created, the delegate is pre-cached and jitted - no more reflection.
Marc Gravell