在C#中,如何实例化方法内部传递的泛型类型?


98

如何在InstantiateType<T>下面的方法中实例化类型T ?

我收到错误消息:“ T”是“类型参数”,但其用法类似于“变量”。

(向下滚动以获取答复)

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

补充答案:

感谢所有评论,他们让我走上了正轨,这就是我想要做的:

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

+1转移到更好的设计模式。
Joel Coehoorn

+1代表非常整洁的代码,很少见。
nawfal

Answers:


131

像这样声明您的方法:

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

请注意最后的附加约束。然后new在方法主体中创建一个实例:

T obj = new T();    

4
多年来,我一直在写C#,并且在日常工作中遭受了一些泛型类型滥用,而且我从不知道您可以定义这样的约束来实例化泛型类型。非常感谢!
Nicolas Martel

非常非常棒!!
Sotiris Zegiannis

如果没有指定类型该怎么办?
jj

31

几种方法。

不指定类型必须具有构造函数:

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

使用构造函数:

T obj = new T();

但这需要以下子句:

where T : new()

1
第一个将分配null而不是为引用类型创建实例。
Joel Coehoorn

1
是的 您需要使用反射来创建没有默认构造函数的类型,所有引用类型的default(T)为null。
Dan C.

1
是的,绝对是为了完整性。
annakata

13

为了扩展上述答案,将where T:new()约束添加到泛型方法将要求T具有公共的无参数构造函数。

如果要避免这种情况-在工厂模式下,有时会迫使其他人通过工厂方法而不是直接通过构造函数-那么,替代方法是使用反射(Activator.CreateInstance...)并将默认构造函数设为私有。但这当然会带来性能损失。


这不是人们第一次否决“所有其他答案” :)
Dan C.

我会承认有时有时会不赞成“竞争性”答案,直到dusgt解决了一个问题:DI猜测(非要点)因果报应会解决它们!
Ruben Bartelink 09年


4

有点老了,但对于其他正在寻找解决方案的人来说,也许这可能很有趣:http : //daniel.wertheim.se/2011/12/29/c-generic-factory-with-support-for-private-constructors/

两种解决方案。一种使用激活器,另一种使用编译的Lambda。

//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;
    }
}

2

您还可以使用反射来获取对象的构造函数并以这种方式实例化:

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

1

使用工厂类用已编译的lamba表达式构建对象:我发现实例化泛型类型的最快方法。

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

这是我建立基准的步骤。

创建我的基准测试方法:

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

我也尝试过使用工厂方法:

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

对于测试,我创建了最简单的类:

public class A { }

要测试的脚本:

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

超过1000000次迭代的结果:

新的A():11ms

FactoryMethod A():275ms

FactoryClass A .Create():56毫秒

Activator.CreateInstance A():235ms

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

备注:我已经使用.NET Framework 4.5和4.6进行了测试(等效结果)。


0

而不是创建实例化类型的函数

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

你本可以这样做的

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

1
这不能回答所问的问题。真正的问题是他需要创建泛型类的新实例。也许这是意料之外的,但似乎您是在说使用初始化程序可以解决原始问题,但事实并非如此。该new()约束仍然需要在泛型类型为您解答工作。
用户

如果您试图提供帮助,并建议此处使用初始化工具,则应将其发布为注释,而不是其他答案。
用户
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.