[C#] 생성자에 매개 변수가 필요한 일반 유형의 인스턴스를 작성 하시겠습니까?

BaseFruit을 허용하는 생성자가있는 경우 int weight이와 같은 일반적인 방법으로 과일 조각을 인스턴스화 할 수 있습니까?

public void AddFruit<T>()where T: BaseFruit{
    BaseFruit fruit = new T(weight); /*new Apple(150);*/
    fruit.Enlist(fruitManager);
}

주석 뒤에 예제가 추가됩니다. BaseFruit매개 변수가없는 생성자를 제공 한 다음 멤버 변수를 통해 모든 것을 채우는 경우에만이 작업을 수행 할 수 있습니다 . 내 실제 코드 (과일에 관한 것이 아님)에서 이것은 오히려 비실용적입니다.

-Update-
그래서 어떤 식 으로든 제약으로 해결할 수없는 것 같습니다. 답변에서 세 가지 후보 솔루션이 있습니다.

  • 공장 패턴
  • 반사
  • 활성제

나는 반사가 가장 깨끗하지 않다고 생각하는 경향이 있지만 다른 두 가지를 결정할 수는 없습니다.



답변

또한 더 간단한 예 :

return (T)Activator.CreateInstance(typeof(T), new object[] { weight });

T에서 new () 제약 조건을 사용하는 것은 컴파일 타임에 컴파일러가 공용 매개 변수없는 생성자를 확인하도록하기위한 것이므로 형식을 만드는 데 사용되는 실제 코드는 Activator 클래스입니다.

존재하는 특정 생성자에 대해 스스로를 확인해야하며 이러한 종류의 요구 사항은 코드 냄새 일 수 있습니다 (또는 오히려 C #의 현재 버전에서는 피해야 할 것).


답변

매개 변수화 된 생성자를 사용할 수 없습니다. ” where T : new()“제약 조건 이있는 경우 매개 변수가없는 생성자를 사용할 수 있습니다 .

고통이지만 그런 삶입니다 🙁

이것이 내가 “정적 인터페이스” 로 해결하고 싶은 것 중 하나입니다 . 그런 다음 정적 메소드, 연산자 및 생성자를 포함하도록 T를 제한 한 다음 호출 할 수 있습니다.


답변

예; 위치를 변경하십시오.

where T:BaseFruit, new()

그러나 이것은 매개 변수가없는 생성자 에서만 작동합니다 . 속성을 설정하는 다른 방법이 있어야합니다 (속성 자체 또는 유사한 설정).


답변

가장 간단한 솔루션
Activator.CreateInstance<T>()


답변

Jon이 지적했듯이 이것은 매개 변수가없는 생성자를 제한하는 삶입니다. 그러나 다른 해결책은 팩토리 패턴을 사용하는 것입니다. 이것은 쉽게 구속 할 수 있습니다

interface IFruitFactory<T> where T : BaseFruit {
  T Create(int weight);
}

public void AddFruit<T>( IFruitFactory<T> factory ) where T: BaseFruit {
  BaseFruit fruit = factory.Create(weight); /*new Apple(150);*/
  fruit.Enlist(fruitManager);
}

또 다른 옵션은 기능적 접근 방식을 사용하는 것입니다. 팩토리 방식으로 전달하십시오.

public void AddFruit<T>(Func<int,T> factoryDel) where T : BaseFruit {
  BaseFruit fruit = factoryDel(weight); /* new Apple(150); */
  fruit.Enlist(fruitManager);
}


답변

리플렉션을 사용하여 수행 할 수 있습니다.

public void AddFruit<T>()where T: BaseFruit
{
  ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) });
  if (constructor == null)
  {
    throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor");
  }
  BaseFruit fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit;
  fruit.Enlist(fruitManager);
}

편집 : 생성자 == null 검사가 추가되었습니다.

편집 : 캐시를 사용하는 더 빠른 변형 :

public void AddFruit<T>()where T: BaseFruit
{
  var constructor = FruitCompany<T>.constructor;
  if (constructor == null)
  {
    throw new InvalidOperationException("Type " + typeof(T).Name + " does not contain an appropriate constructor");
  }
  var fruit = constructor.Invoke(new object[] { (int)150 }) as BaseFruit;
  fruit.Enlist(fruitManager);
}
private static class FruitCompany<T>
{
  public static readonly ConstructorInfo constructor = typeof(T).GetConstructor(new Type[] { typeof(int) });
}


답변

user1471935의 제안에 추가하여 :

하나 이상의 매개 변수가있는 생성자를 사용하여 일반 클래스를 인스턴스화하려면 Activator 클래스를 사용할 수 있습니다.

T instance = Activator.CreateInstance(typeof(T), new object[] {...}) 

개체 목록은 제공하려는 매개 변수입니다. Microsoft에 따르면 :

CreateInstance […]는 지정된 매개 변수와 가장 일치하는 생성자를 사용하여 지정된 유형의 인스턴스를 작성합니다.

CreateInstance ( CreateInstance<T>()) 의 일반 버전도 있지만 생성자 매개 변수를 제공 할 수 없습니다.