LB.
LB.

Reputation: 14112

Passing arguments to C# generic new() of templated type

I'm trying to create a new object of type T via its constructor when adding to the list.

I'm getting a compile error: The error message is:

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

But my classes do have a constructor argument! How can I make this work?

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       tabListItems.Add(new T(listItem)); // error here.
   } 
   ...
}

Upvotes: 491

Views: 270272

Answers (17)

Michael B.
Michael B.

Reputation: 145

Just to expand on the Expression tree answer a little bit. If you want to avoid param list and typed delegates, you can use generics and plain old static classes to cache all of your expressions.

The first usage of a type and/or arguments does takes more time, because of compiling, but afterwards the speed is unmatched.

public static class New<T>
{
    private readonly static Type TypeOfT = typeof(T);
    public static T Create()
    {
        return Build.Instantiate();
    }
    public static T Create<Arg1>(Arg1 arg1)
    {
        return Build<Arg1>.Instantiate(arg1);
    }

    public static T Create<Arg1, Arg2>(Arg1 arg1, Arg2 arg2)
    {
        return Build<Arg1, Arg2>.Instantiate(arg1, arg2);
    }

    public static Func<T> Construct()
    {
        return Build.Constructor;
    }

    public static Func<Arg1, T> Construct<Arg1>()
    {
        return Build<Arg1>.Constructor;
    }

    public static Func<Arg1, Arg2, T> Construct<Arg1, Arg2>()
    {
        return Build<Arg1, Arg2>.Constructor;
    }

    private static class Helper
    {
        public static Tuple<NewExpression, ParameterExpression[]> CreateExpressions(Type[] argsTypes)
        {
            var constructorInfo = TypeOfT.GetConstructor(argsTypes);
            var constructorParameters = argsTypes.Select(p => Expression.Parameter(p)).ToArray();
            var expression = Expression.New(constructorInfo, constructorParameters);
            return new Tuple<NewExpression, ParameterExpression[]>(expression, constructorParameters);
        }
    }

    private static class Build
    {
        public static readonly Func<T> Constructor = Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();
        public static T Instantiate() => Constructor.Invoke();
    }

    private static class Build<Arg1>
    {
        public static readonly Func<Arg1, T> Constructor = CreateConstructor();
        public static T Instantiate(Arg1 arg) => Constructor.Invoke(arg);
        private static Func<Arg1, T> CreateConstructor()
        {
            var expressionAndParam = Helper.CreateExpressions(new Type[] { typeof(Arg1) });
            return Expression.Lambda<Func<Arg1, T>>(expressionAndParam.Item1, expressionAndParam.Item2).Compile();
        }
    }

    private static class Build<Arg1, Arg2>
    {
        public static Func<Arg1, Arg2, T> Constructor = CreateConstructor();
        public static T Instantiate(Arg1 arg1, Arg2 arg2) => Constructor.Invoke(arg1, arg2);
        private static Func<Arg1, Arg2, T> CreateConstructor()
        {
            var expressionAndParam = Helper.CreateExpressions(new Type[] { typeof(Arg1), typeof(Arg2) });
            return Expression.Lambda<Func<Arg1, Arg2, T>>(expressionAndParam.Item1, expressionAndParam.Item2).Compile();
        }
    // removed the rest of the code for brevity but you
    // would use the same pattern to continue with as many 
    // expected arguments as you may need
}

Example of usage.

// create the list with initial capacity
var list = New<List<string>>.Create(10);
        
// constructor for int as a Func
Func<int> integerConstructor = New<int>.Construct();
int x = integerConstructor.Invoke();

MyClass myclass = New<MyClass>.Create("hello", "world");

Upvotes: 0

kolis
kolis

Reputation: 584

Using the static abstract interface methods feature in C# 11 it is possible to build a workaround, using a factory pattern. For example:

public interface IFactory<TSelf> where TSelf : IFactory<TSelf> {
  static abstract TSelf New(int i, string s);
}

public struct Foo : IFactory<Foo> {

  public static Foo New(int i, string s) {
    return new Foo(i, s);
  }

  public readonly int I;
  public readonly string S;

  public Foo(int i, string s) {
    I = i;
    S = s;
  }
}

public static class Maker {
  public static T Make<T>(int i, string s) where T : IFactory<T> {
    return T.New(i, s);
  }
}

A limitation of this approach is that it can only be used on classes that you own, since you need to implement your specific factory interface on the target classes.

Upvotes: 4

Franz
Franz

Reputation: 87

Supplementary performance information

Performance test of database access and filling of data model class with the expression method of Jeroen van Langen (see above) and direct instantiation of the data model class.

Conclusion: The expression method is faster.

Results:

  1. Test: direct instance of data model class: Records: 3558, seconds: 1.2746019
  2. Test: instance with method read list with type parameter: records: 3558, seconds: 0.4878858

Code example of the expression method:

var list = ReadList<DataModel>(SQLStatement, Connection);

Method ReadList: Note: all data model class has a constructor with parameter type of SQLDataReader

public static List<pDataModel> ReadList<pDataModel>(string pSQLStatement, SqlConnection pConnection) where pDataModel : new()
    {
            // constructor of data model
            var lType = typeof(pDataModel);
            var lParameters = new Type[] { typeof(SqlDataReader) };
            var lDataModelConstructor = CreateConstructor(lType, lParameters);

            // read data
            List<pDataModel> lDataList = new List<pDataModel>();
            using (pConnection)
            {
                SqlCommand lCommand;
                lCommand = new SqlCommand(pSQLStatement, pConnection);
                pConnection.Open();
                SqlDataReader lReader = lCommand.ExecuteReader();


                if (lReader.HasRows)
                {
                    while (lReader.Read())
                    {
                        pDataModel lDataItem = (pDataModel)lDataModelConstructor(lReader);
                        lDataList.Add(lDataItem);
                    }
                }
                lReader.Close();
                pConnection.Close();
            }

            return lDataList;
    }

Code example of the direct instantiation:

           List<DataModel> list= new List<DataModel>();
            using (connection)
            {
                SqlCommand command;
                command = new SqlCommand(SQLStatement, connection);
                connection.Open();
                SqlDataReader reader = command.ExecuteReader();
                if (reader.HasRows)
                {
                    while (reader.Read())
                    {
                        list.Add(new DataModel(reader));
                    }
                }
                reader.Close();
                connection.Close();
            }

Upvotes: -1

Jeroen van Langen
Jeroen van Langen

Reputation: 22073

Very old question, but new answer ;-)

The ExpressionTree version: (I think the fastests and cleanest solution)

Like Welly Tambunan said, "we could also use expression tree to build the object"

This will generate a 'constructor' (function) for the type/parameters given. It returns a delegate and accept the parameter types as an array of objects.

Here it is:

// this delegate is just, so you don't have to pass an object array. _(params)_
public delegate object ConstructorDelegate(params object[] args);

public static ConstructorDelegate CreateConstructor(Type type, params Type[] parameters)
{
    // Get the constructor info for these parameters
    var constructorInfo = type.GetConstructor(parameters);

    // define a object[] parameter
    var paramExpr = Expression.Parameter(typeof(Object[]));

    // To feed the constructor with the right parameters, we need to generate an array 
    // of parameters that will be read from the initialize object array argument.
    var constructorParameters = parameters.Select((paramType, index) =>
        // convert the object[index] to the right constructor parameter type.
        Expression.Convert(
            // read a value from the object[index]
            Expression.ArrayAccess(
                paramExpr,
                Expression.Constant(index)),
            paramType)).ToArray();

    // just call the constructor.
    var body = Expression.New(constructorInfo, constructorParameters);

    var constructor = Expression.Lambda<ConstructorDelegate>(body, paramExpr);
    return constructor.Compile();
}

Example MyClass:

public class MyClass
{
    public int TestInt { get; private set; }
    public string TestString { get; private set; }

    public MyClass(int testInt, string testString)
    {
        TestInt = testInt;
        TestString = testString;
    }
}

Usage:

// you should cache this 'constructor'
var myConstructor = CreateConstructor(typeof(MyClass), typeof(int), typeof(string));

// Call the `myConstructor` function to create a new instance.
var myObject = myConstructor(10, "test message");

enter image description here


Another example: passing the types as an array

var type = typeof(MyClass);
var args = new Type[] { typeof(int), typeof(string) };

// you should cache this 'constructor'
var myConstructor = CreateConstructor(type, args);

// Call the `myConstructor` fucntion to create a new instance.
var myObject = myConstructor(10, "test message");

DebugView of Expression

.Lambda #Lambda1<TestExpressionConstructor.MainWindow+ConstructorDelegate>(System.Object[] $var1) {
    .New TestExpressionConstructor.MainWindow+MyClass(
        (System.Int32)$var1[0],
        (System.String)$var1[1])
}

This is equivalent to the code that is generated:

public object myConstructor(object[] var1)
{
    return new MyClass(
        (System.Int32)var1[0],
        (System.String)var1[1]);
}

Small downside

All valuetypes parameters are boxed when they are passed like an object array.


Simple performance test:

private void TestActivator()
{
    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < 1024 * 1024 * 10; i++)
    {
        var myObject = Activator.CreateInstance(typeof(MyClass), 10, "test message");
    }
    sw.Stop();
    Trace.WriteLine("Activator: " + sw.Elapsed);
}

private void TestReflection()
{
    var constructorInfo = typeof(MyClass).GetConstructor(new[] { typeof(int), typeof(string) });

    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < 1024 * 1024 * 10; i++)
    {
        var myObject = constructorInfo.Invoke(new object[] { 10, "test message" });
    }

    sw.Stop();
    Trace.WriteLine("Reflection: " + sw.Elapsed);
}

private void TestExpression()
{
    var myConstructor = CreateConstructor(typeof(MyClass), typeof(int), typeof(string));

    Stopwatch sw = Stopwatch.StartNew();

    for (int i = 0; i < 1024 * 1024 * 10; i++)
    {
        var myObject = myConstructor(10, "test message");
    }

    sw.Stop();
    Trace.WriteLine("Expression: " + sw.Elapsed);
}

TestActivator();
TestReflection();
TestExpression();

Results:

Activator: 00:00:13.8210732
Reflection: 00:00:05.2986945
Expression: 00:00:00.6681696

Using Expressions is +/- 8 times faster than Invoking the ConstructorInfo and +/- 20 times faster than using the Activator

Upvotes: 39

NightmareZ
NightmareZ

Reputation: 41

If all is you need is convertion from ListItem to your type T you can implement this convertion in T class as conversion operator.

public class T
{
    public static implicit operator T(ListItem listItem) => /* ... */;
}

public static string GetAllItems(...)
{
    ...
    List<T> tabListItems = new List<T>();
    foreach (ListItem listItem in listCollection) 
    {
        tabListItems.Add(listItem);
    } 
    ...
}

Upvotes: 0

fljx
fljx

Reputation: 141

If you simply want to initialise a member field or property with the constructor parameter, in C# >= 3 you can do it very easier:

public static string GetAllItems<T>(...) where T : InterfaceOrBaseClass, new() 
{ 
   ... 
   List<T> tabListItems = new List<T>(); 
   foreach (ListItem listItem in listCollection)  
   { 
       tabListItems.Add(new T{ BaseMemberItem = listItem }); // No error, BaseMemberItem owns to InterfaceOrBaseClass. 
   }  
   ... 
} 

This is the same thing Garry Shutler said, but I'd like to put an aditional note.

Of course you can use a property trick to do more stuff than just setting a field value. A property "set()" can trigger any processing needed to setup its related fields and any other need for the object itself, including a check to see if a full initialization is to take place before the object is used, simulating a full contruction (yes, it is an ugly workaround, but it overcomes M$'s new() limitation).

I can't be assure if it is a planned hole or an accidental side effect, but it works.

It is very funny how MS people adds new features to the language and seems to not do a full side effects analysis. The entire generic thing is a good evidence of this...

Upvotes: 7

Daniel M&#246;ller
Daniel M&#246;ller

Reputation: 86630

If you have access to the class you're going to use, you can use this approach which I used.

Create an interface that has an alternative creator:

public interface ICreatable1Param
{
    void PopulateInstance(object Param);
}

Make your classes with an empty creator and implement this method:

public class MyClass : ICreatable1Param
{
    public MyClass() { //do something or nothing }
    public void PopulateInstance (object Param)
    {
        //populate the class here
    }
}

Now use your generic methods:

public void MyMethod<T>(...) where T : ICreatable1Param, new()
{
    //do stuff
    T newT = new T();
    T.PopulateInstance(Param);
}

If you don't have access, wrap the target class:

public class MyClass : ICreatable1Param
{
    public WrappedClass WrappedInstance {get; private set; }
    public MyClass() { //do something or nothing }
    public void PopulateInstance (object Param)
    {
        WrappedInstance = new WrappedClass(Param);
    }
}

Upvotes: 7

Anders
Anders

Reputation: 744

I sometimes use an approach that resembles to the answers using property injection, but keeps the code cleaner. Instead of having a base class/interface with a set of properties, it only contains a (virtual) Initialize()-method that acts as a "poor man's constructor". Then you can let each class handle it's own initialization just as a constructor would, which also adds a convinient way of handling inheritance chains.

If often find myself in situations where I want each class in the chain to initialize its unique properties, and then call its parent's Initialize()-method which in turn initializes the parent's unique properties and so forth. This is especially useful when having different classes, but with a similar hierarchy, for example business objects that are mapped to/from DTO:s.

Example that uses a common Dictionary for initialization:

void Main()
{
    var values = new Dictionary<string, int> { { "BaseValue", 1 }, { "DerivedValue", 2 } };

    Console.WriteLine(CreateObject<Base>(values).ToString());

    Console.WriteLine(CreateObject<Derived>(values).ToString());
}

public T CreateObject<T>(IDictionary<string, int> values)
    where T : Base, new()
{
    var obj = new T();
    obj.Initialize(values);
    return obj;
}

public class Base
{
    public int BaseValue { get; set; }

    public virtual void Initialize(IDictionary<string, int> values)
    {
        BaseValue = values["BaseValue"];
    }

    public override string ToString()
    {
        return "BaseValue = " + BaseValue;
    }
}

public class Derived : Base
{
    public int DerivedValue { get; set; }

    public override void Initialize(IDictionary<string, int> values)
    {
        base.Initialize(values);
        DerivedValue = values["DerivedValue"];
    }

    public override string ToString()
    {       
        return base.ToString() + ", DerivedValue = " + DerivedValue;
    }
}

Upvotes: 0

chris31389
chris31389

Reputation: 9356

I found that I was getting an error "cannot provide arguments when creating an instance of type parameter T" so I needed to do this:

var x = Activator.CreateInstance(typeof(T), args) as T;

Upvotes: 7

user287107
user287107

Reputation: 9417

in .Net 3.5 and after you could use the activator class:

(T)Activator.CreateInstance(typeof(T), args)

Upvotes: 378

James Jones
James Jones

Reputation: 8783

Since nobody bothered to post the 'Reflection' answer (which I personally think is the best answer), here goes:

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       Type classType = typeof(T);
       ConstructorInfo classConstructor = classType.GetConstructor(new Type[] { listItem.GetType() });
       T classInstance = (T)classConstructor.Invoke(new object[] { listItem });

       tabListItems.Add(classInstance);
   } 
   ...
}

Edit: This answer is deprecated due to .NET 3.5's Activator.CreateInstance, however it is still useful in older .NET versions.

Upvotes: 53

Tim Lehner
Tim Lehner

Reputation: 15251

Object initializer

If your constructor with the parameter isn't doing anything besides setting a property, you can do this in C# 3 or better using an object initializer rather than calling a constructor (which is impossible, as has been mentioned):

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       tabListItems.Add(new T() { YourPropertyName = listItem } ); // Now using object initializer
   } 
   ...
}

Using this, you can always put any constructor logic in the default (empty) constructor, too.

Activator.CreateInstance()

Alternatively, you could call Activator.CreateInstance() like so:

public static string GetAllItems<T>(...) where T : new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
        object[] args = new object[] { listItem };
        tabListItems.Add((T)Activator.CreateInstance(typeof(T), args)); // Now using Activator.CreateInstance
   } 
   ...
}

Note that Activator.CreateInstance can have some performance overhead that you may want to avoid if execution speed is a top priority and another option is maintainable to you.

Upvotes: 34

Silas Davis
Silas Davis

Reputation: 742

This is kind of mucky, and when I say kind of mucky I may mean revolting, but supposing you can furnish your parameterised type with an empty constructor, then:

public static T GetTInstance<T>() where T: new()
{
    var constructorTypeSignature = new Type[] {typeof (object)};
    var constructorParameters = new object[] {"Create a T"};
    return (T) new T().GetType().GetConstructor(constructorTypeSignature).Invoke(constructorParameters);
}

Will effectively allow you to construct an object from a parameterised type with an argument. In this case I am assuming the constructor I want has a single argument of type object. We create a dummy instance of T using the constraint permitted empty constructor and then use reflection to get one of its other constructors.

Upvotes: 0

Garry Shutler
Garry Shutler

Reputation: 32698

This will not work in your situation. You can only specify the constraint that it has an empty constructor:

public static string GetAllItems<T>(...) where T: new()

What you could do is use property injection by defining this interface:

public interface ITakesAListItem
{
   ListItem Item { set; }
}

Then you could alter your method to be this:

public static string GetAllItems<T>(...) where T : ITakesAListItem, new()
{
   ...
   List<T> tabListItems = new List<T>();
   foreach (ListItem listItem in listCollection) 
   {
       tabListItems.Add(new T() { Item = listItem });
   } 
   ...
}

The other alternative is the Func method described by JaredPar.

Upvotes: 20

JaredPar
JaredPar

Reputation: 755317

In order to create an instance of a generic type in a function you must constrain it with the "new" flag.

public static string GetAllItems<T>(...) where T : new()

However that will only work when you want to call the constructor which has no parameters. Not the case here. Instead you'll have to provide another parameter which allows for the creation of object based on parameters. The easiest is a function.

public static string GetAllItems<T>(..., Func<ListItem,T> del) {
  ...
  List<T> tabListItems = new List<T>();
  foreach (ListItem listItem in listCollection) 
  {
    tabListItems.Add(del(listItem));
  }
  ...
}

You can then call it like so

GetAllItems<Foo>(..., l => new Foo(l));

Upvotes: 454

klkitchens
klkitchens

Reputation: 1242

I believe you have to constraint T with a where statement to only allow objects with a new constructor.

RIght now it accepts anything including objects without it.

Upvotes: -3

Richard
Richard

Reputation: 1169

You need to add where T: new() to let the compiler know that T is guaranteed to provide a default constructor.

public static string GetAllItems<T>(...) where T: new()

Upvotes: 7

Related Questions