Pass the parameter to C_generic new() of the template type

I'm trying to create a new object of type T through its constructor when I add it to the list.

I received a compilation error: The error message is:

'T': cannot provide parameters when creating variable instances

But my class does have a constructor parameter!How can I do this?

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.
   } 
   ...
}

#1st floor

If you only want to initialize member fields or attributes using constructor parameters, you can do this more easily in C#> = 3:

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 as what Gary Teller said, but I would like to make an additional note.

Of course, in addition to setting field values, you can use attribute techniques to do more.The property "set()" triggers any processing required to set its associated fields and any other need for the object itself, including checking whether the object is fully initialized before it is used, and simulating a complete construction (yes, this is an ugly solution, but it overcomes the new () limitation of $M).

I can't guarantee if it's a planned hole or an accidental side effect, but it does work.

It's interesting how MS staff add new functionality to the language and don't seem to have a complete side-effect analysis.The whole thing in common is good evidence of this...

#2nd floor

Since no one bothered to post a "rethink" answer (which I personally believe is the best), here are:

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 has been discarded because of Activator.CreateInstance for.NET 3.5, but it is still useful in older versions of.NET.

#3rd floor

I found that I received the error "Cannot provide parameters when creating an instance of type parameter T", so I need to do this:

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

#4th floor

Old question, but new answer; -)

ExpressionTree version: (I think the fastest and cleanest solution)

As Welly Tambunan said, "We can also use expression trees to build objects"

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

Here 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` fucntion to create a new instance.
var myObject = myConstructor(10, "test message");

Another example: passing a type 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");

Expression for DebugView

.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 generated code:

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

Small drawbacks

All valuetypes parameters are boxed when passed like an array of objects.

Simple performance testing:

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();

Result:

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

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

#5th floor

This is a bit dirty, and when I say dirty I may mean disgust, but suppose you can provide parameterized types with empty constructors, 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);
}

This effectively allows you to construct objects from parameterized types using parameters.In this case, I assume that the constructor I want has an object parameter of type.We create a virtual instance of T using an empty constructor allowed by the constraint, then use reflection to get the other constructors.

Tags: Lambda Attribute

Posted on Tue, 11 Feb 2020 23:07:23 -0500 by groovything