12

이 질문에는 이미 답변이 있습니다.

런타임시 결정되는 유형의 인스턴스를 생성하는 가장 좋은 방법은 무엇입니까 (.NET 4).

BaseClass 객체에 대한 작업이 파생 클래스의 인스턴스에 의해 호출 될 수 있지만 인스턴스 메서드가 있습니다. 같은 유형의 다른 인스턴스를 생성해야합니다.this메서드 내에서. 파생 된 각 클래스에 대해 메서드를 오버로드하는 것은 상당히 복잡하므로 단일 구현을 유지하는 것이 더 효율적입니다.

public class BaseClass
{
     //constructors + properties + methods etc

     public SomeMethod()
     {
          //some code

          DerivedClass d = new DerivedClass(); //ideally determine the DerivedClass type at run-time
     }
}

리플렉션이나 동적 키워드 사용에 대해 조금 읽었지만 경험이 없습니다.


7 답변


10

당신이 찾고있는Activator.CreateInstance(다른 오버로드도 있습니다.이 하나생성자 인수를 받아 들인다). 그래서 네가 쓸 수있어.

var anotherOneLikeMe = Activator.CreateInstance(this.GetType());

여기에 문제가있을 수 있습니다.anotherOneLikeMe다음과 같이 입력됩니다.object공통 기본 클래스로 캐스팅하지 않으면 (예 :BaseClass귀하의 예에서) 당신이 할 수있는 일은별로 없습니다.


20

런타임에 인스턴스를 반복적으로 만드는 성능을위한 가장 좋은 방법은 컴파일 된 표현식입니다.

static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
 ).Compile();

X x = YCreator();

통계 (2012) :

    Iterations: 5000000
    00:00:00.8481762, Activator.CreateInstance(string, string)
    00:00:00.8416930, Activator.CreateInstance(type)
    00:00:06.6236752, ConstructorInfo.Invoke
    00:00:00.1776255, Compiled expression
    00:00:00.0462197, new

통계 (2015, .net 4.5, x64) :

    Iterations: 5000000
    00:00:00.2659981, Activator.CreateInstance(string, string)
    00:00:00.2603770, Activator.CreateInstance(type)
    00:00:00.7478936, ConstructorInfo.Invoke
    00:00:00.0700757, Compiled expression
    00:00:00.0286710, new

통계 (2015, .net 4.5, x86) :

    Iterations: 5000000
    00:00:00.3541501, Activator.CreateInstance(string, string)
    00:00:00.3686861, Activator.CreateInstance(type)
    00:00:00.9492354, ConstructorInfo.Invoke
    00:00:00.0719072, Compiled expression
    00:00:00.0229387, new

전체 코드 :

public static X CreateY_New()
{
  return new Y();
}

public static X CreateY_CreateInstance()
{
  return (X)Activator.CreateInstance(typeof(Y));
}

public static X CreateY_CreateInstance_String()
{
  return (X)Activator.CreateInstance("Program", "Y").Unwrap();
}

static readonly System.Reflection.ConstructorInfo YConstructor = 
    typeof(Y).GetConstructor(Type.EmptyTypes);
static readonly object[] Empty = new object[] { };
public static X CreateY_Invoke()
{
  return (X)YConstructor.Invoke(Empty);
}

static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
 ).Compile();
public static X CreateY_CompiledExpression()
{
  return YCreator();
}

static void Main(string[] args)
{
  const int iterations = 5000000;

  Console.WriteLine("Iterations: {0}", iterations);

  foreach (var creatorInfo in new [] 
    { 
      new {Name = "Activator.CreateInstance(string, string)", Creator = (Func<X>)CreateY_CreateInstance},
      new {Name = "Activator.CreateInstance(type)", Creator = (Func<X>)CreateY_CreateInstance},
      new {Name = "ConstructorInfo.Invoke", Creator = (Func<X>)CreateY_Invoke},
      new {Name = "Compiled expression", Creator = (Func<X>)CreateY_CompiledExpression},
      new {Name = "new", Creator = (Func<X>)CreateY_New},
    })
  {
    var creator = creatorInfo.Creator;

    var sum = 0;
    for (var i = 0; i < 1000; i++)
      sum += creator().Z;

    var stopwatch = new Stopwatch();
    stopwatch.Start();
    for (var i = 0; i < iterations; ++i)
    {
      var x = creator();
      sum += x.Z;
    }
    stopwatch.Stop();
    Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
  }
}

public class X
{
  public X() { }
  public X(int z) { this.Z = z; }
  public int Z;
}
public class Y : X { }


  • 여기에서 확인한 것처럼 액티베이터보다 생성자가 호출하는 것을보기 좋았습니다.blogs.msdn.com/b/haibo_luo/archive/2005/11/17/494009.aspx그리고 여기bloggingabout.net/blogs/vagif/archive/2010/04/02/… - nawfal
  • 6.6236752 Invoke 옵션의 첫 번째 6은 오타입니까? - Jeff
  • @ 제프, 나는 또한 " 그냥 옳을 수 없다고 생각했습니다! "라고 생각했습니다. 그러나 나는 다음과 같이 보입니다. 코드를 Linqpad에 붙여 넣었습니다. 최적화가 가능하고 결과가 다소 일치했습니다 (3 년이 경과 한 경우). DarkGray의 예 : 00 : 00 : 00.4287503, 00 : 00 : 00.4431353과 같은 순서로,00 : 00 : 01.0507991, 00 : 00 : 00.0809354 및 00 : 00 : 00.0231360. - easuter
  • 흠, 나는 그의 코드를 실행해야만하지만, 내 자신의 실험에서 ConstructorInfo.Invoke는 다소 빠르다. 특히 액티베이터를 사용하는 것에 비해. - Jeff
  • @easuter 네, 맞아. 단지 그것이 정말로 느리다는 것을 확인했습니다. 얼마나 짜증나. - Jeff

4

나는 이것이 반영으로 표시되었음을 안다. 그러나 나는 일반적으로 성능과 복잡성의 이유로 최후의 수단으로 반영을 본다. 디자인 / 사용에 반영이 필요한 경우가 있습니다. 그러나 고려해야 할 몇 가지 대안을 제시 할 것입니다.

공장 사용Func:

public void SomeMethod(Func<BaseClass> createDerived)
{
    BaseClass d = createDerived();
}

메소드가 제한된 제네릭 유형을 사용하도록합니다.

public void SomeMethod<TDerived>() where TDerived : BaseClass, new()
{
    TDerived d = new TDerived();
}

커버 아래에서이 마지막 대안은Activator.CreateInstance다른 사람들이 제안했듯이. 둘 다 매개 변수가없는 생성자가 필요하기 때문에 마지막으로 리플렉션을 선호하지만 컴파일러에서는 파생 된 유형에 매개 변수없는 생성자가 있어야한다는 제약 조건을 적용합니다. 반면 리플렉션 방식으로 인해 런타임 예외가 발생합니다.


  • 또한 공장Func접근 방식은 파생 클래스에서 매개 변수화 된 생성자를 호출 할 수있는 람다 식에 대해 열려 있습니다. 예 :SomeMethod(() => new MyDerived(DateTime.Now); - devgeezer

2

여기서 문제는 유형을 알 수 없다는 것입니다.DerivedClass컴파일 할 때.

그러나 다음과 같은 유형의 작업을 수행 할 수 있습니다.

BaseClass obj = new DerivedClass();

이것은 다음과 같이 구현됩니다.

BaseClass obj = (BaseClass)Activator.CreateInstance(this.GetType());

DerivedClass에 매개 변수없는 생성자가 없으면이 호출은 실패합니다.


1

이것은 정말로 당신이 "런타임"과 목표가 무엇을 의미하는지에 달려 있습니다. 예를 들어, Jon과 Bas는 모두 Reflection을 사용하여 특정 클래스를 후기 바인딩하고 런타임에 인스턴스화하도록하는 아이디어에 중점을 둡니다. 이것은 확실히 하나의 아이디어이며 런타임에 객체의 메소드를 발견 할 수도 있습니다. 이것이 목표라면

인터페이스를 사용하는 경우 몇 가지 추가 옵션이 있습니다. Microsoft Extensibility Framework (또는 MEF)는 런타임시 발견 가능성 및 인스턴스화 기능을 포함하고 있으므로 사용자가보고자하는 옵션 일 수 있습니다. 단점은 발견 된 클래스가 올바른 인터페이스를 따라야한다는 것이지만, 대부분의 경우 실제 문제는 아닙니다.

로드하는 클래스를 알고 있고 인터페이스 (공통 테마)가 같지만 런타임에 다른 클래스를 인스턴스화하려는 경우 IoC 컨테이너가 옵션입니다. 이것은 특히 당신이 요구하는 것이 아닙니다.

동적 키워드는 당신이 찾고있는 키워드가 아닙니다. 그것은 런타임에로드되지만 dyanmic은 호출하는 메소드가 실제로 존재하는지 컴파일러가 확인하지 않는 것에 대한 것입니다. 잘못 처리하면 존재하지 않는 메소드를 호출 할 때 흥미로운 폭발로 끝날 수 있습니다. 다이나믹에 대한 가장 중요한 동기는 IronPython과 같은 동적 언어와의 상호 작용입니다.


0

public void SomeMethod()
{
    object x =Activator.CreateInstance(this.GetType());
}

이것은 새로운 인스턴스를 생성해야합니다, 다른 한편으로 나는 왜 당신이 이것을하려고하는지 궁금합니다.


0

실제로 당신은Activator.CreateInstance당신은 구체적으로Activator.CreateInstance (String, String)런타임 중에 알 수있는 클래스 이름을 사용하여 호출 할 수 있습니다.

기본 유형에서 파생 형을 인스턴스화하는 경우이 방법이 가장 유용합니다. 너는 부름을받는다면SomeMethod파생 된 유형 자체에서 다음 이전 답변을 사용하여this.GetType()충분해야합니다.

연결된 질문


관련된 질문

최근 질문