In C#, how to instantiate a passed generic type inside a method?

C#Generics

C# Problem Overview


How can I instantiate the type T inside my InstantiateType<T> method below?

I'm getting the error: 'T' is a 'type parameter' but is used like a 'variable'.:

(SCROLL DOWN FOR REFACTORED ANSWER)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestGeneric33
{
    class Program
    {
        static void Main(string[] args)
        {
            Container container = new Container();
            Console.WriteLine(container.InstantiateType<Customer>("Jim", "Smith"));
            Console.WriteLine(container.InstantiateType<Employee>("Joe", "Thompson"));
            Console.ReadLine();
        }
    }

    public class Container
    {
        public T InstantiateType<T>(string firstName, string lastName) where T : IPerson
        {
            T obj = T();
            obj.FirstName(firstName);
            obj.LastName(lastName);
            return obj;
        }

    }

    public interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }

    public class Customer : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Company { get; set; }
    }

    public class Employee : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int EmployeeNumber { get; set; }
    }
}

REFACTORED ANSWER:

Thanks for all the comments, they got me on the right track, this is what I wanted to do:

using System;

namespace TestGeneric33
{
    class Program
    {
        static void Main(string[] args)
        {
            Container container = new Container();
            Customer customer1 = container.InstantiateType<Customer>("Jim", "Smith");
            Employee employee1 = container.InstantiateType<Employee>("Joe", "Thompson");
            Console.WriteLine(PersonDisplayer.SimpleDisplay(customer1));
            Console.WriteLine(PersonDisplayer.SimpleDisplay(employee1));
            Console.ReadLine();
        }
    }

    public class Container
    {
        public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
        {
            T obj = new T();
            obj.FirstName = firstName;
            obj.LastName = lastName;
            return obj;
        }
    }

    public interface IPerson
    {
        string FirstName { get; set; }
        string LastName { get; set; }
    }

    public class PersonDisplayer
    {
        private IPerson _person;

        public PersonDisplayer(IPerson person)
        {
            _person = person;
        }

        public string SimpleDisplay()
        {
            return String.Format("{1}, {0}", _person.FirstName, _person.LastName);
        }

        public static string SimpleDisplay(IPerson person)
        {
            PersonDisplayer personDisplayer = new PersonDisplayer(person);
            return personDisplayer.SimpleDisplay();
        }
    }

    public class Customer : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Company { get; set; }
    }

    public class Employee : IPerson
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int EmployeeNumber { get; set; }
    }
}

C# Solutions


Solution 1 - C#

Declare your method like this:

public string InstantiateType<T>(string firstName, string lastName) 
              where T : IPerson, new()

Notice the additional constraint at the end. Then create a new instance in the method body:

T obj = new T();    

Solution 2 - C#

Couple of ways.

Without specifying the type must have a constructor:

T obj = default(T); //which will produce null for reference types

With a constructor:

T obj = new T();

But this requires the clause:

where T : new()

Solution 3 - C#

To extend on the answers above, adding where T:new() constraint to a generic method will require T to have a public, parameterless constructor.

If you want to avoid that - and in a factory pattern you sometimes force the others to go through your factory method and not directly through the constructor - then the alternative is to use reflection (Activator.CreateInstance...) and keep the default constructor private. But this comes with a performance penalty, of course.

Solution 4 - C#

you want new T(), but you'll also need to add , new() to the where spec for the factory method

Solution 5 - C#

A bit old but for others looking for a solution, perhaps this could be of interest: http://daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-private-constructors/

Two solutions. One using Activator and one using Compiled Lambdas.

//Person has private ctor
var person = Factory<Person>.Create(p => p.Name = "Daniel");

public static class Factory<T> where T : class 
{
	private static readonly Func<T> FactoryFn;

	static Factory()
	{
		//FactoryFn = CreateUsingActivator();

		FactoryFn = CreateUsingLambdas();
	}

	private static Func<T> CreateUsingActivator()
	{
		var type = typeof(T);

		Func<T> f = () => Activator.CreateInstance(type, true) as T;

		return f;
	}

	private static Func<T> CreateUsingLambdas()
	{
		var type = typeof(T);

		var ctor = type.GetConstructor(
			BindingFlags.Instance | BindingFlags.CreateInstance |
			BindingFlags.NonPublic,
			null, new Type[] { }, null);

		var ctorExpression = Expression.New(ctor);
		return Expression.Lambda<Func<T>>(ctorExpression).Compile();
	}

	public static T Create(Action<T> init)
	{
		var instance = FactoryFn();

		init(instance);

		return instance;
	}
}

Solution 6 - C#

You can also use reflection to fetch the object's constructor and instantiate that way:

var c = typeof(T).GetConstructor();
T t = (T)c.Invoke();

Solution 7 - C#

Using a factory class to build your object with compiled lamba expression: The fastest way I've found to instantiate generic type.

public static class FactoryContructor<T>
{
    private static readonly Func<T> New =
        Expression.Lambda<Func<T>>(Expression.New(typeof (T))).Compile();

    public static T Create()
    {
        return New();
    }
}

Here is the steps I followed to set up the benchmark.

Create my benchmark test method:

static void Benchmark(Action action, int iterationCount, string text)
{
    GC.Collect();
    var sw = new Stopwatch();
    action(); // Execute once before

    sw.Start();
    for (var i = 0; i <= iterationCount; i++)
    {
        action();
    }

    sw.Stop();
    System.Console.WriteLine(text + ", Elapsed: {0}ms", sw.ElapsedMilliseconds);
}

I've also tried using a factory method:

public static T FactoryMethod<T>() where T : new()
{
    return new T();
}

For the tests I've created the simplest class :

public class A { }

The script to test:

const int iterations = 1000000;
Benchmark(() => new A(), iterations, "new A()");
Benchmark(() => FactoryMethod<A>(), iterations, "FactoryMethod<A>()");
Benchmark(() => FactoryClass<A>.Create(), iterations, "FactoryClass<A>.Create()");
Benchmark(() => Activator.CreateInstance<A>(), iterations, "Activator.CreateInstance<A>()");
Benchmark(() => Activator.CreateInstance(typeof (A)), iterations, "Activator.CreateInstance(typeof (A))");

> Results over 1 000 000 iterations:

> new A(): 11ms

> FactoryMethod A(): 275ms

> FactoryClass A .Create(): 56ms

> Activator.CreateInstance A (): 235ms

> Activator.CreateInstance(typeof (A)): 157ms

Remarks: I've tested using both .NET Framework 4.5 and 4.6 (equivalent results).

Solution 8 - C#

Instead of creating a function to Instantiate the type

public T InstantiateType<T>(string firstName, string lastName) where T : IPerson, new()
    {
        T obj = new T();
        obj.FirstName = firstName;
        obj.LastName = lastName;
        return obj;
    }

you could have done it like this

T obj = new T { FirstName = firstName, LastName = lastname };

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEdward TanguayView Question on Stackoverflow
Solution 1 - C#Joel CoehoornView Answer on Stackoverflow
Solution 2 - C#annakataView Answer on Stackoverflow
Solution 3 - C#Dan C.View Answer on Stackoverflow
Solution 4 - C#Ruben BartelinkView Answer on Stackoverflow
Solution 5 - C#DanielView Answer on Stackoverflow
Solution 6 - C#pimView Answer on Stackoverflow
Solution 7 - C#ThomasView Answer on Stackoverflow
Solution 8 - C#TMulView Answer on Stackoverflow