2009-11-30 11 views
19

나는 다음과 같은 C# 테스트 코드가 있습니다C# : 생성자가있는 일반 유형?

class MyItem 
    { 
    MyItem(int a) {} 
    } 

    class MyContainer<T> 
    where T : MyItem, new() 
    { 
    public void CreateItem() 
    { 
     T oItem = new T(10); 
    } 
    } 

스튜디오를 컴파일 할 수 없습니다 비주얼 오류가 사용되는 경우 '새로운'라인에 있습니다 :

'T': cannot provide arguments when creating an instance of a variable type 

가에 C#으로 가능 매개 변수가없는 생성자를 사용하여 제네릭 형식의 개체를 만드시겠습니까? C++에서 그런 것을하는 것은 문제가되지 않습니다. 그래서 나는 C#에서 똑같은 일을 할 수없는 이유가 궁금합니다. 어쩌면 추가 'where'가 필요하거나 구문이 다른 것일까 요?

답변

16

그것은이 반사 함께 할 수있는 전화 사이트 : T 원하는 생성자를 구현하는

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

그러나이없는 일반적인 제약 때문에, 보장하기 위해 인터페이스를 구현하는 모든 유형에서 해당 생성자를주의해서 선언하지 않는 한이 작업을 수행하는 것이 좋습니다. 내가 사용하는

+0

필자가 기억 하듯'new()'제약 조건은'Activator.CreateInstance()'호출로 해석됩니다. –

9

일반적인 제약 조건이 없으므로 직접 적용 할 수 없습니다 (CLR 제한 사항). 이것을 원한다면, 팩토리없는 클래스 (팩토리없는 생성자를 가짐)를 제공해야하며,이를 제 2 제네릭 타입 매개 변수로 전달해야합니다.

25

C# 및 VB.Net과 관련하여 특정 매개 변수가있는 생성자를 갖도록 제네릭을 제한한다는 개념을 지원하지 않습니다. 빈 생성자를 갖는 제약 만 지원합니다.

발신자에게 값을 생성하기 위해 공장 람다를 전달하는 것이 하나의 해결 방법입니다. 예를 들어

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

CreateItem(x => new SomeClass(x)); 
+0

매우 똑똑한 접근! – Greg

2

한 패턴이 제약 클래스는 적절한 서명 init 메소드를 정의하는 인터페이스를 구현하는 것입니다 : IMO

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

일반적으로 좋지 않은 아이디어입니다. 이제 생성되었지만 초기화되지 않은 객체 일 수 있기 때문입니다. –

4

을, 여기에 가장 좋은 방법은 즉,

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); 
    } 
} 
초기화 방법

그러나 Expression 당신이 원하는 것을 할 수 있습니다 (그러나없이 컴파일 타임 지원) :

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

이 메서드는 성능을 위해 대리자를 캐시하고 재사용합니다.

+0

이것은'Activator.CreateInstance()'사용과 크게 다른가요? 내가 올바르게 이해한다면 컴파일 타임 지원을하지 못하며 예상되는 생성자가 존재하지 않으면 런타임 오류가 발생합니다. – Greg

+0

@Greg - 예를 들어 롯을 많이 사용하는 경우 (예 : 팩토리) : 일단 생성되면 대리자가 미리 캐시되고 지터가 생성되어 더 이상 반영되지 않습니다. –