【问题标题】:In C#, how to instantiate a passed generic type inside a method?在 C# 中,如何在方法中实例化传递的泛型类型?
【发布时间】:2010-10-14 02:57:38
【问题描述】:

如何在下面的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; }
    }
}

重构答案:

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

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 用于转向更好的设计模式。
  • +1 表示代码非常整洁,非常罕见。

标签: c# generics


【解决方案1】:

像这样声明你的方法:

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

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

T obj = new T();    

【讨论】:

  • 多年来,我一直在编写 C# 并经常滥用泛型类型,我从不知道您可以定义这样的约束来实例化泛型类型。非常感谢!
  • 非常非常好!!
  • 如果没有指定类型怎么办,这可能吗?
【解决方案2】:

几种方法。

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

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

使用构造函数:

T obj = new T();

但这需要子句:

where T : new()

【讨论】:

  • 第一个将分配 null 而不是为引用类型创建实例。
  • 是的。您需要使用反射来创建没有默认构造函数的类型,所有引用类型的 default(T) 为 null。
  • 是的,确实是为了完整性。
【解决方案3】:

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

如果你想避免这种情况——在工厂模式中你有时会强制其他人通过你的工厂方法而不是直接通过构造函数——那么替代方法是使用反射 (Activator.CreateInstance...) 并保留默认构造函数私人的。但这当然会带来性能损失。

【讨论】:

  • 这不是人们第一次对“所有其他答案”投反对票 :)
  • 我承认有时会粗鲁地不赞成“竞争”的答案,直到 dusgt 解决了一个问题:D 我想(非分数)业力会解决它们!
【解决方案4】:

您想要 new T(),但您还需要将 , new() 添加到工厂方法的 where 规范中

【讨论】:

  • 我把它备份了,我理解它,帮助,似乎一般人们更喜欢发布代码而不是这里的描述
  • 谢谢,世界又变得有意义了!
  • 正确,但诚然您的回答有点偏短;)
【解决方案5】:

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

两种解决方案。一种使用 Activator,另一种使用 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;
    }
}

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

      【解决方案7】:

      使用工厂类通过编译的 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))");
      

      超过 1 000 000 次迭代的结果:

      新的 A():11 毫秒

      FactoryMethod A(): 275ms

      FactoryClass A .Create(): 56ms

      Activator.CreateInstance A (): 235ms

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

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

      【讨论】:

        【解决方案8】:

        而不是创建一个函数来实例化类型

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

        【讨论】:

        • 这不能回答所提出的问题。这里真正的问题是他需要创建一个泛型类的新实例。也许这是无意的,但是您似乎在说使用初始化程序可以解决原始问题,但事实并非如此。泛型类型仍然需要new() 约束才能使您的答案起作用。
        • 如果您想提供帮助并建议初始化程序在这里是一个有用的工具,那么您应该将其作为评论发布,而不是其他答案。
        猜你喜欢
        • 1970-01-01
        • 2023-03-19
        • 2017-12-23
        • 1970-01-01
        • 1970-01-01
        • 2021-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多