[펌][C#] Activator.CreateInstance와 new 차이

seung-jae hwang·2019년 3월 12일
0

Csharp

목록 보기
1/8

From : https://debuglog.tistory.com/34

Activator.CreateInstance와 new 차이
C#에서 Singleton 패턴을 구현할 때 Generic type을 받아 구현하려면 System.Activator.CreateInstance 메소드를 사용하게된다.

//example
instance = System.Activator.CreateInstance (typeof(T)) as T;
여기서 CreateInstance()와 new T()의 차이가 궁금해졌다.
CreateInstance()는 Singleton 예제를 찾다가 발견했는데 이게 뭔지도 모르고 쓰기엔 찝찝함이 느껴진다.

완벽히 이해했는지는 자신없지만 Microsoft Documentation과 Stackoverflow를 계속 찾아보면서 어느정도 결론을 내렸다.

CreateInstance()
CreateInstance() 제네릭 메소드는 type parameters로 지정된 형식의 인스턴스화를 위해 컴파일러에서 사용한다. 예를들어, new T()와 같은 제네릭 메소드는 CreateInstance() 제네릭 메소드를 사용한다.

영문을 번역하다보니 제대로 이해하기는 어렵지만, new T() 와 CreateInstance()는 기능상 같다고 이해할 수 있다. stackoverflow에서도 비슷한 견해를 가진 사람을 많았다.

Performance 차이
그렇다면 new T()와 CreateInstance() 간의 성능차이는 있을까?

실험을 위해 다음 4가지 Test 구간을 만들어서 비교해보았다.

예제
using UnityEngine;
using System.Diagnostics;
using System;

public class CreateInstanceTest : MonoBehaviour {

public class TestClass
{
}

const int iterCount = 100000000;

void Start () {

Stopwatch sw = System.Diagnostics.Stopwatch.StartNew ();

//warm up
Test1();
Test2();
Test3();
Test4();
sw.Stop ();

//Test1 : new T();
sw.Reset();
sw.Start ();
for (int i = 0; i < iterCount; i++) {
Test1 ();
}
sw.Stop ();
UnityEngine.Debug.Log(string.Format("Test 1 : {0}", sw.ElapsedMilliseconds));

//Test2 : Activator.CreateInstance;
sw.Reset();
sw.Start ();
for (int i = 0; i < iterCount; i++) {
Test2 ();
}
sw.Stop ();
UnityEngine.Debug.Log(string.Format("Test 2 : {0}", sw.ElapsedMilliseconds));

//Test3 : generic new();
sw.Reset();
sw.Start ();
for (int i = 0; i < iterCount; i++) {
Test3();
}
sw.Stop ();
UnityEngine.Debug.Log(string.Format("Test 3 : {0}", sw.ElapsedMilliseconds));

//Test4 : generic Activator.CreateInstance;
sw.Reset();
sw.Start ();
for (int i = 0; i < iterCount; i++) {
Test4();
}
sw.Stop ();
UnityEngine.Debug.Log(string.Format("Test 4 : {0}", sw.ElapsedMilliseconds));

}

/*
 * GC.KeepAlive(obj) :
 * 현재 루틴이 시작된 지점에서 이 메소드가 호출된 지점까지 가비지콜렉션을 불가능하도록 합니다.
 */

void Test1(){
    var obj = new TestClass ();
    GC.KeepAlive (obj);
}


void Test2(){
var obj = Activator.CreateInstance ();
GC.KeepAlive (obj);
}

void Test3() where T : new(){
var obj = new T ();
GC.KeepAlive (obj);
}

void Test4(){
var obj = (T)Activator.CreateInstance (typeof(T));
GC.KeepAlive (obj);
}
}

결과

클래스타입을 명시해주고 new() 메소드로 생성한 Test1가 압도적으로 빠른 성능을 보였다.

CreateInstance()의 non-generic method인 Test2와 generic method인 Test4는 거의 차이가 없었다.

generic method로 구현한 new(T)인 Test3은 Test2와 Test4보다 약간 느렸다.

결론
런타임에서 클래스타입을 알고 있으면 new 클래스로 생성해주는 것이 좋다.

제네릭 메소드로 구현하려면 Activator.CreateInstance()를 사용하는 게 좋다.

그러나 테스트 코드는 100,000,000 (1억번) 반복을 통해 측정되었고, 결과를 비교해봤을 때 치명적일 만큼 performance 이슈를 가진다고 할 수 는 없다.

개인적으로 개발자 입장에서 유지보수하기 좋은 코드가 좋은 코드라 생각하고, 이러한 측면을 고려해서 프로젝트 상황에 맞게 판단해서 사용하면 될 것 같다.

참조
MSDN : Activator.CreateInstance Method ()

​Does System.Activator.CreateInstance(T) have performance issues big enough to discourage us from using it casually?

0개의 댓글