C#: Generic types that have a constructor?

后端 未结 5 1363
一个人的身影
一个人的身影 2021-02-02 12:28

I have the following C# test code:

  class MyItem
  {
    MyItem( int a ) {}
  }

  class MyContainer< T >
    where T : MyItem, new()
  {
    public void          


        
相关标签:
5条回答
  • 2021-02-02 12:56

    C#, and VB.Net for that matter, do not support the notion of constraining a generic to have a constructor with specific parameters. It only supports constraining to have an empty constructor.

    One work around is to have the caller pass in a factory lambda to create the value. For instance

    public void CreateItem(Func<int,T> del) {
      T oItem = del(10);
    }
    

    Call site

    CreateItem(x => new SomeClass(x));
    
    0 讨论(0)
  • 2021-02-02 13:10

    One pattern I use is to have the constrained class implement an interface which defines an Init method with the appropriate signature:

    interface IMyItem
    {
        void Init(int a);
    }
    
    class MyItem : IMyItem
    {
        MyItem() {}
        void Init(int a) { }
    }    
    
    class MyContainer< T >
        where T : MyItem, IMyItem, new()
    {
        public void CreateItem()
        {
            T oItem = new T();
            oItem.Init( 10 );
        }
    }
    
    0 讨论(0)
  • 2021-02-02 13:11

    There's no such generic constraint, so it's not possible directly (this is a CLR limitation). If you want this, you have to provide a factory class (which has a parameterless constructor), and pass it as a second generic type parameter.

    0 讨论(0)
  • 2021-02-02 13:12

    It can be done with reflection:

    public void CreateItem()
    {
      int constructorparm1 = 10;
      T oItem = Activator.CreateInstance(typeof(T), constructorparm1) as T;
    }
    

    But there is no generic constraint to ensure that T implements the desired constructor, so I wouldn't advise doing this unless you are careful to declare that constructor in every type that implements the interface.

    0 讨论(0)
  • 2021-02-02 13:15

    IMO, the best approach here is an initialize method, i.e.

    interface ISomeInterface {
        void Init(int i);
    }
    class Foo : ISomeInterface {
        void ISomeInterface.Init(int i) { /* ... */ }
    }
    static class Program {
        static T Create<T>(int i) where T : class, ISomeInterface, new() {
            T t = new T();
            t.Init(i);
            return t;
        }
        static void Main() {
            Foo foo = Create<Foo>(123);
        }
    }
    

    However, you can do what you want with Expression (but without compile-time support):

    using System;
    using System.Linq.Expressions;
    class Foo {
        public Foo(int i) { /* ... */ }
    }
    static class Program {
        static T Create<T>(int i) {
            return CtorCache<T>.Create(i);
        }
        static class CtorCache<T> {
            static Func<int, T> ctor;
            public static T Create(int i) {
                if (ctor == null) ctor = CreateCtor();
                return ctor(i);
            }
            static Func<int, T> CreateCtor() {
                var param = Expression.Parameter(typeof(int), "i");
                var ci = typeof(T).GetConstructor(new[] {typeof(int)});
                if(ci == null) throw new InvalidOperationException("No such ctor");
                var body = Expression.New(ci, param);
                return Expression.Lambda<Func<int, T>>(body, param).Compile();
            }
        }
        static void Main() {
            Foo foo = Create<Foo>(123);
        }
    }
    

    Note that this caches and reuses the delegate for performance.

    0 讨论(0)
提交回复
热议问题