30

나는 우리 응용 프로그램의 성능을 향상 시키려고 노력하고 있습니다. Activator.CreateInstance 호출이 많아서 슬픔이 생깁니다.

인터페이스 (ITabDocument)를 기반으로 많은 클래스를 인스턴스화하고이 코드를 사용하여 주위를 둘러 보았습니다.

이 코드는 우리가 가진 Activator.CreateInstance 코드를 사용하는 것보다 낫지는 않습니다.

    public static Func<T> CreateInstance<T>(Type objType) where T : class, new()
    {
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        return (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
    }

왜 내가이 모든 일을하는지 궁금해.

ITabDocument document = CreateInstance<ITabDocument>(Type.GetType("[Company].Something"));

위와 관련하여 도움이되는 객체를 만드는 더 좋은 방법이 있습니까? 콘크리트 타입이 확실하지 않을 때 조금 힘듭니다.


  • 얼마나 자주 당신입니까?부름 CreateInstance- 그게 핵심은 당신이 전화하고 있기 때문이야.CreateInstance한 번 그러나 그 때생각해 내다공장 대표. 전화를 거는 경우CreateInstance각 작업마다 다음 예, 느려질 수 있습니다 ... - Jon Skeet
  • 공장 패턴, 추상 공장 및 유사한 창조 패턴과 같은 일부 디자인 패턴이 도움이 될 수 있습니다. 그들은 당신이 당신의 객체를 늦게 묶을 수있게 해줍니다. 당신은 여기에서 볼 수 있었다 :oodesign.com - Nickolodeon
  • @Jon Skeet 답장을 보내 주셔서 감사합니다. 처음에는 많은 탭 문서를 만들고 ITabbedDocument의 모든 구현이 다르므로 20-30 번 이상 호출해야합니다. - Tiffany Townsend
  • @ 티파니 :하지만 그들은 모두 다른 유형입니까? 이것이 중요한 요소입니다. 유형별로 한 번만 호출해야합니다. - Jon Skeet
  • 죄송합니다, 예 그들은 모두 다른 유형입니다. 예제를 수정했습니다. - Tiffany Townsend

5 답변


42

나는 이들 사이에서 벤치마킹을했다 (나는 최소한의 세부 사항을 적어 둔다).

public static T Instance() //~1800 ms
{
    return new T();
}

public static T Instance() //~1800 ms
{
    return new Activator.CreateInstance<T>();
}

public static readonly Func<T> Instance = () => new T(); //~1800 ms

public static readonly Func<T> Instance = () => 
                                 Activator.CreateInstance<T>(); //~1800 ms

//works for types with no default constructor as well
public static readonly Func<T> Instance = () => 
               (T)FormatterServices.GetUninitializedObject(typeof(T)); //~2000 ms


public static readonly Func<T> Instance = 
     Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();  
     //~50 ms for classes and ~100 ms for structs

CD에 따르면 컴파일 된 표현이 가장 빠르며 큰 차이가 있습니다. 모든 메소드를 제외하고(T)FormatterServices.GetUninitializedObject(typeof(T)) 기본 생성자가있는 유형에서만 작동합니다.

제네릭 형식 당 정적 클래스가 있으면 컴파일 된 결과 대리자를 캐싱하는 것은 간단합니다. 처럼:

public static class New<T> where T : new()
{
    public static readonly Func<T> Instance = Expression.Lambda<Func<T>>
                                              (
                                               Expression.New(typeof(T))
                                              ).Compile();
}

참고 사항new강제. 무엇이든 불러라.

MyType me = New<MyType>.Instance();

클래스가 메모리에 처음으로로드되는 것을 제외하고는 실행이 가장 빠릅니다.

기본 생성자와 함께 두 유형 모두를 처리하는 클래스를 갖기 위해, 나는 하이브리드 접근 방식을 취했습니다.여기에서:

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

효율적인 방식으로 값 유형을 처리합니다.

유의 사항(T)FormatterServices.GetUninitializedObject(t)실패 할 것이다string. 따라서 빈 문자열을 반환하기 위해 string에 대한 특수 처리가 제 위치에 있습니다.


  • 그리고이 질문은 런타임에 유형별로 만 알려진 객체를 만드는 데 어떻게 적용됩니까? generics를 사용하는 질문은 반환 된 객체를 기본 클래스로 캐스팅하기위한 것입니다. 제공 한 예제에서 반환되는 인스턴스 유형은 generic 매개 변수와 동일하므로 일반 생성자를 사용할 수 있습니다 (초기화되지 않은 객체도 만들 수 있다는 사실 제외). - Richard Collette
  • @RichardCollette 나는 그 질문에 직접 대답하지 않는다는 것에 동의하지만, 좋은 작은 도우미 클래스 (클래스는 불완전하다, 잘하면 나는 그것을 하루에 업데이트 할 것이다)를 갖는 방법을 보여준다. 그럼에도 불구하고 OP 용 솔루션으로 미세 조정하기가 너무 어렵지 않습니다.typeof(T)). - nawfal
  • 첫 번째 것은 단순한 일반 T () ... 올바른 것은 아닙니다. 이것은 람다 버전보다 느릴 수 없습니다. 제네릭은 컴파일 타임에 처리되므로 비 제너릭 생성자만큼 빠릅니다. 그것을 다시 확인할 수 있습니까? 중요한 점은 모든 게시물의 결과에 의심의 여지가 있기 때문입니다. - Timo
  • @Timo는 "제네릭이 컴파일 타임에 처리됩니다."라는 의미에 달려 있습니다. 일부 정적 검사가 완료됩니다. 그러나 T는 항상 런타임 유형을가집니다. 필자는 컴파일러가 일반적인 경우의 리플렉션에 의존하지 않을 정도로 영리해야한다고 생각하지만 유감스럽게도 그렇습니다. 그게 전부 요점입니다. 또한 일리노이를 검사하여이를 확인할 수 있습니다. - nawfal
  • 이 블로그 읽기codeblog.jonskeet.uk/2011/08/22/…Jon Skeet 님. 또한 내 결과를 확인하는 모든 의견의 사용자 별 다양한 벤치 마크 또한 Roslyn에서 행동이 어떻게 바뀌 었는지 그 블로그에서 저의 의견을 주목하십시오. - nawfal

15

도움이 될 수 있습니다.Activator.CreateInstance 또는 ConstructorInfo.Invoke를 사용하지 말고 컴파일 된 람다 식을 사용하십시오.:

// Make a NewExpression that calls the ctor with the args we just created
NewExpression newExp = Expression.New(ctor, argsExp);                  

// Create a lambda with the New expression as body and our param object[] as arg
LambdaExpression lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param);            


// Compile it
ObjectActivator compiled = (ObjectActivator)lambda.Compile();


  • 고마워, 나는 이것을 더 일찍 보았지만, 나는 내가 필요로하는 것처럼 특별하게 부를 수 있을지 확신하지 못했다. - Tiffany Townsend
  • 링크 주셔서 감사합니다, 그것은이 블로그에 리드rogeralsing.com/2008/02/28/linq-expressions-creating-objects제가 지금까지 읽은 블로그는 지금까지 본 최고의 블로그 중 하나라고 생각합니다. 정말 고맙습니다 - RichK
  • 이것은 이론적으로 질문에 대답 할 수 있지만,그것은 바람직 할 것이다.여기에 답변의 핵심 부분을 포함하고 참조 용 링크를 제공하십시오. 답변을 수정하여 문제를 해결 한 다음 " 운영자 개입이 필요 "라고 플래그를 지정하십시오. 삭제 삭제를 요청하십시오. - Matt

7

문제는 어딘가에 결과를 저장하고 그 결과를 반복해서 사용하는 대신에 CreateInstance를 계속해서 반복적으로 호출 할 것이므로 아마도 그 안에 캐싱 할 것입니다.

internal static class DelegateStore<T> {
     internal static IDictionary<string, Func<T>> Store = new ConcurrentDictionary<string,Func<T>>();
}

public static T CreateInstance<T>(Type objType) where T : class
{
    Func<T> returnFunc;
    if(!DelegateStore<T>.Store.TryGetValue(objType.FullName, out returnFunc)) {
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        returnFunc = (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
        DelegateStore<T>.Store[objType.FullName] = returnFunc;
    }
    return returnFunc();
}


  • 사전에 사전이 필요하지 않습니다.DelegateStore<T>왜냐하면 그 범용 클래스는 자동적으로 유일한 per-T이미 인스턴스화. 그것이 사실 일지라도 이것은 사실입니다.static수업. 그래서 당신이 필요로하는 모든 것DelegateStore<T>간단하게internal static Func<T> _cached_func;가지고 계시다시피, 많은 사전을 만드십시오 - 모든 것을위한 새로운 것T- 캐시 된 하나의 대리자 만 포함하는 각. - Glenn Slayden

3

같은 코드가 생성되면 오버 헤드가 발생할 수 있습니다.

그만큼ILGenerator팩토리에 대한 코드를 동적으로 생성합니다.

지도의 일부 또는Dictionary이미 사용한 유형의 유형을 만들고 해당 유형에 대해 생성 된 팩토리 메소드를 유지합니다.


0

대리자를 생성하고 생성자를 직접 호출하는 일반적인 방법입니다. 제공된 대리자 형식의 서명을 사용하여 지정된 형식의 생성자를 자동으로 검색하고 해당 형식의 대리자를 만듭니다. 여기 코드 :

/// <summary>
/// Reflective object construction helper.
/// All methods are thread safe.
/// </summary>
public static class Constructor
{
    /// <summary>
    /// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType.
    /// Instance is casted to delegateTypes's return type. 
    /// Delegate's return type must be assignable from instanceType.
    /// </summary>
    /// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param>
    /// <param name="instanceType">Type of instance to be constructed.</param>
    /// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns>
    public static Delegate Compile(Type delegateType,Type instanceType)
    {
        if (!typeof(Delegate).IsAssignableFrom(delegateType))
        {
            throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType");
        }
        var invoke = delegateType.GetMethod("Invoke");
        var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray();
        var resultType = invoke.ReturnType;
        if(!resultType.IsAssignableFrom(instanceType))
        {
            throw new ArgumentException(String.Format("Delegate's return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName));
        }
        var ctor = instanceType.GetConstructor(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
        if(ctor == null)
        {
            throw new ArgumentException("Can't find constructor with delegate's signature","instanceType");
        }
        var parapeters = parameterTypes.Select(Expression.Parameter).ToArray();

        var newExpression = Expression.Lambda(delegateType,
            Expression.Convert(Expression.New(ctor, parapeters), resultType),
            parapeters);
        var @delegate = newExpression.Compile();
        return @delegate;
    }
    public static TDelegate Compile<TDelegate>(Type instanceType)
    {
        return (TDelegate) (object) Compile(typeof (TDelegate), instanceType);
    }
}

의 일부입니다Yappi프로젝트의 출처. 이것을 사용하여 매개 변수가있는 생성자 (ref 및 out 매개 변수 제외)를 포함하여 지정된 유형의 모든 생성자를 호출하는 대리자를 생성 할 수 있습니다.

샘플 사용법 :

var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>));
var list = newList(100);

대리자를 만든 후 정적 사전이나 generic 매개 변수가있는 클래스의 정적 필드에 어딘가에 저장합니다. 매번 새로운 대리인을 구성하지 마십시오. 주어진 유형의 여러 인스턴스를 구성하는 데 하나의 대리자를 사용하십시오.

연결된 질문


관련된 질문

최근 질문