IT story

제네릭 형식의 인스턴스를 만드시겠습니까?

hot-time 2020. 5. 1. 08:05
반응형

제네릭 형식의 인스턴스를 만드시겠습니까?


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

최근에 비슷한 문제가 발생했습니다. 우리 솔루션을 모두와 공유하고 싶었습니다. Car<CarA>열거 형이있는 json 객체에서 인스턴스를 만들고 싶었습니다 .

Dictionary<MyEnum, Type> mapper = new Dictionary<MyEnum, Type>();

mapper.Add(1, typeof(CarA));
mapper.Add(2, typeof(BarB)); 

public class Car<T> where T : class
{       
    public T Detail { get; set; }
    public Car(T data)
    {
       Detail = data;
    }
}
public class CarA
{  
    public int PropA { get; set; }
    public CarA(){}
}
public class CarB
{
    public int PropB { get; set; }
    public CarB(){}
}

var jsonObj = {"Type":"1","PropA":"10"}
MyEnum t = GetTypeOfCar(jsonObj);
Type objectT = mapper[t]
Type genericType = typeof(Car<>);
Type carTypeWithGenerics = genericType.MakeGenericType(objectT);
Activator.CreateInstance(carTypeWithGenerics , new Object[] { JsonConvert.DeserializeObject(jsonObj, objectT) });

As an addition to user1471935's suggestion:

To instantiate a generic class by using a constructor with one or more parameters, you can now use the Activator class.

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

The list of objects are the parameters you want to supply. According to Microsoft:

CreateInstance [...] creates an instance of the specified type using the constructor that best matches the specified parameters.

There's also a generic version of CreateInstance (CreateInstance<T>()) but that one also does not allow you to supply constructor parameters.


I created this method:

public static V ConvertParentObjToChildObj<T,V> (T obj) where V : new()
{
    Type typeT = typeof(T);
    PropertyInfo[] propertiesT = typeT.GetProperties();
    V newV = new V();
    foreach (var propT in propertiesT)
    {
        var nomePropT = propT.Name;
        var valuePropT = propT.GetValue(obj, null);

        Type typeV = typeof(V);
        PropertyInfo[] propertiesV = typeV.GetProperties();
        foreach (var propV in propertiesV)
        {
            var nomePropV = propV.Name;
            if(nomePropT == nomePropV)
            {
                propV.SetValue(newV, valuePropT);
                break;
            }
        }
    }
    return newV;
}

I use that in this way:

public class A 
{
    public int PROP1 {get; set;}
}

public class B : A
{
    public int PROP2 {get; set;}
}

Code:

A instanceA = new A();
instanceA.PROP1 = 1;

B instanceB = new B();
instanceB = ConvertParentObjToChildObj<A,B>(instanceA);

It is still possible, with high performance, by doing the following:

    //
    public List<R> GetAllItems<R>() where R : IBaseRO, new() {
        var list = new List<R>();
        using ( var wl = new ReaderLock<T>( this ) ) {
            foreach ( var bo in this.items ) {
                T t = bo.Value.Data as T;
                R r = new R();
                r.Initialize( t );
                list.Add( r );
            }
        }
        return list;
    }

and

    //
///<summary>Base class for read-only objects</summary>
public partial interface IBaseRO  {
    void Initialize( IDTO dto );
    void Initialize( object value );
}

The relevant classes then have to derive from this interface and initialize accordingly. Please note, that in my case, this code is part of a surrounding class, which already has <T> as generic parameter. R, in my case, also is a read-only class. IMO, the public availability of Initialize() functions has no negative effect on the immutability. The user of this class could put another object in, but this would not modify the underlying collection.

참고URL : https://stackoverflow.com/questions/731452/create-instance-of-generic-type

반응형