Как создать новый экземпляр объекта из Типа

571

Можно не всегда знать Type объекта во время компиляции, но может потребоваться создать экземпляр Type. Как вы получаете новый экземпляр объекта от Type?

Теги:
performance
reflection
types

12 ответов

757
Лучший ответ

Класс Activator в корневом пространстве имен System довольно эффективен.

Существует много перегрузок для передачи параметров конструктору и тому подобное. Проверьте документацию по адресу:

http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx

или (новый путь)

https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance

Вот несколько простых примеров:

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");
  • 18
    Рад, что, наконец, нашел это, но второй вызов не совсем правильно, пропуская кавычки и обратные пармы, должно быть: ObjectType instance = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespace.ObjectType");
  • 8
    Вам нужно вызвать 'Unwrap ()', чтобы получить фактический тип объекта, который вам нужен: ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap ();
Показать ещё 3 комментария
114
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

Класс Activator имеет общий вариант, который делает это немного проще:

ObjectType instance = Activator.CreateInstance<ObjectType>();
  • 11
    За исключением того, что это не работает для времени выполнения Type t .
  • 5
    @Kevin Конечно. Такая операция не может работать на статически типизированном языке, потому что она не имеет смысла. Вы не можете вызывать методы для объекта неизвестного типа. В то же время (= так как пишу этот ответ) C # получил dynamic конструкцию , которая делает такие конструкции позволяют , но в большинстве случаев этот ответ до сих пор охватывает его.
Показать ещё 4 комментария
91

Скомпилированное выражение - лучший способ! (для производительности многократно создавать экземпляр во время выполнения).

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

Статистика (2017, LINQPad 5.22.02/x64/.NET 4.6):

    Iterations: 5000000
    No args
    00:00:00.3897563, Activator.CreateInstance(string assemblyName, string typeName)
    00:00:00.3500748, Activator.CreateInstance(Type type)
    00:00:01.0100714, ConstructorInfo.Invoke
    00:00:00.1375767, Compiled expression
    00:00:00.1337920, Compiled expression (type)
    00:00:00.0593664, new
    Single arg
    00:00:03.9300630, Activator.CreateInstance(Type type)
    00:00:01.3881770, ConstructorInfo.Invoke
    00:00:00.1425534, Compiled expression
    00:00:00.0717409, new

Полный код:

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

static X CreateY_New_Arg(int z)
{
    return new Y(z);
}

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

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

static X CreateY_CreateInstance_Arg(int z)
{
    return (X)Activator.CreateInstance(typeof(Y), new object[] { z, });
}

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

private static readonly System.Reflection.ConstructorInfo YConstructor_Arg =
    typeof(Y).GetConstructor(new[] { typeof(int), });
static X CreateY_Invoke_Arg(int z)
{
    return (X)YConstructor_Arg.Invoke(new object[] { z, });
}

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

private static readonly Func<X> YCreator_Type = Expression.Lambda<Func<X>>(
   Expression.New(typeof(Y))
).Compile();
static X CreateY_CompiledExpression_Type()
{
    return YCreator_Type();
}

private static readonly ParameterExpression YCreator_Arg_Param = Expression.Parameter(typeof(int), "z");
private static readonly Func<int, X> YCreator_Arg = Expression.Lambda<Func<int, X>>(
   Expression.New(typeof(Y).GetConstructor(new[] { typeof(int), }), new[] { YCreator_Arg_Param, }),
   YCreator_Arg_Param
).Compile();
static X CreateY_CompiledExpression_Arg(int z)
{
    return YCreator_Arg(z);
}

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

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

    Console.WriteLine("No args");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(string assemblyName, string typeName)", Creator = (Func<X>)CreateY_CreateInstance},
        new {Name = "Activator.CreateInstance(Type 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 = "Compiled expression (type)", Creator = (Func<X>)CreateY_CompiledExpression_Type},
        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);
    }

    Console.WriteLine("Single arg");
    foreach (var creatorInfo in new[]
    {
        new {Name = "Activator.CreateInstance(Type type)", Creator = (Func<int, X>)CreateY_CreateInstance_Arg},
        new {Name = "ConstructorInfo.Invoke", Creator = (Func<int, X>)CreateY_Invoke_Arg},
        new {Name = "Compiled expression", Creator = (Func<int, X>)CreateY_CompiledExpression_Arg},
        new {Name = "new", Creator = (Func<int, X>)CreateY_New_Arg},
    })
    {
        var creator = creatorInfo.Creator;

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

        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; ++i)
        {
            var x = creator(i);
            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
{
    public Y() {}
    public Y(int z) : base(z) {}
}
  • 11
    +1 за всю статистику! На данный момент мне не нужно такое представление, но все равно очень интересно. :)
  • 0
    Также есть TypeDescriptor.CreateInstance (см. Stackoverflow.com/a/17797389/1242 ), который может быть быстрее, если используется с TypeDescriptor.AddProvider.
Показать ещё 4 комментария
38

Одна из реализаций этой проблемы заключается в попытке вызвать конструктор без параметров типа:

public static object GetNewObject(Type t)
{
    try
    {
        return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return null;
    }
}

Вот тот же подход, который содержится в общем методе:

public static T GetNewObject<T>()
{
    try
    {
        return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
    }
    catch
    {
        return default(T);
    }
}
  • 15
    Исключительное программирование? Это кажется очень плохой реализацией, когда вы можете просто размышлять над типом, чтобы определить конструкторы.
12

Это довольно просто. Предположим, что ваше имя класса Car и пространство имен Vehicles, затем передайте параметр как Vehicles.Car, который возвращает объект типа Car. Подобным образом вы можете создать любой экземпляр любого класса динамически.

public object GetInstance(string strNamesapace)
{         
     Type t = Type.GetType(strNamesapace); 
     return  Activator.CreateInstance(t);         
}

Если ваше Полностью квалифицированное имя (т.е. Vehicles.Car в этом случае) находится в другой сборке, Type.GetType будет иметь значение null. В таких случаях вы прокручиваете все сборки и находите Type. Для этого вы можете использовать приведенный ниже код

public object GetInstance(string strFullyQualifiedName)
{
     Type type = Type.GetType(strFullyQualifiedName);
     if (type != null)
         return Activator.CreateInstance(type);
     foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
     {
         type = asm.GetType(strFullyQualifiedName);
         if (type != null)
             return Activator.CreateInstance(type);
     }
     return null;
 }

И вы можете получить экземпляр, вызвав вышеуказанный метод.

object objClassInstance = GetInstance("Vehicles.Car");
  • 0
    Во втором случае (внешняя сборка) вы можете просто передать «Vehicles.Car, OtherAssembly» своему первому методу, и он будет работать. Очевидно, OtherAssembly - это название сборки, в которой он живет.
  • 1
    @danmiser Это требует жесткого кодирования имени сборки. Чтобы реализовать гибкость, я проверяю ноль, и код работает динамически :)
12

Если это для чего-то, что будет много называться в экземпляре приложения, намного быстрее выполнить компиляцию и кеширование динамического кода вместо использования активатора или ConstructorInfo.Invoke(). Два простых варианта динамической компиляции компилируются выражения Linq или некоторые простые IL opcodes и DynamicMethod. В любом случае разница будет огромной, когда вы начнете получать плотные петли или несколько вызовов.

  • 0
    Ссылка "IL opcodes and DynamicMethod" не работает.
9

Без использования Reflection:

private T Create<T>() where T : class, new()
{
    return new T();
}
  • 2
    Чем это полезно? Вы уже должны знать тип для вызова этого метода, и если вы знаете тип, вы можете создать его без специального метода.
  • 0
    Так что T может меняться во время выполнения. Полезно, если вы работаете с производными типами.
Показать ещё 5 комментариев
7

Если вы хотите использовать конструктор по умолчанию, то решение с использованием System.Activator, представленное ранее, вероятно, наиболее удобно. Однако, если в типе отсутствует конструктор по умолчанию или вы должны использовать нестандартный, тогда опция должна использовать отражение или System.ComponentModel.TypeDescriptor. В случае отражения достаточно знать только имя типа (с его пространством имен).

Пример с использованием отражения:

ObjectType instance = 
    (ObjectType)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(
        typeName: objectType.FulName, // string including namespace of the type
        ignoreCase: false,
        bindingAttr: BindingFlags.Default,
        binder: null,  // use default binder
        args: new object[] { args, to, constructor },
        culture: null, // use CultureInfo from current thread
        activationAttributes: null
    );

Пример использования TypeDescriptor:

ObjectType instance = 
    (ObjectType)System.ComponentModel.TypeDescriptor.CreateInstance(
        provider: null, // use standard type description provider, which uses reflection
        objectType: objectType,
        argTypes: new Type[] { types, of, args },
        args: new object[] { args, to, constructor }
    );
7

Не работает ли общий T t = new T();?

  • 8
    На самом деле, это было бы в общем классе / методе, но не для данного «Типа».
  • 0
    Предполагается, что тип T имеет ограничение 'new ()'.
5

Учитывая эту проблему, активатор будет работать, когда имеется без параметров ctor. Если это ограничение, используйте

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()
3

Я могу решить этот вопрос, потому что я искал реализацию простого метода CloneObject для произвольного класса (со стандартным конструктором)

С помощью общего метода вы можете потребовать, чтобы тип реализовал New().

Public Function CloneObject(Of T As New)(ByVal src As T) As T
    Dim result As T = Nothing
    Dim cloneable = TryCast(src, ICloneable)
    If cloneable IsNot Nothing Then
        result = cloneable.Clone()
    Else
        result = New T
        CopySimpleProperties(src, result, Nothing, "clone")
    End If
    Return result
End Function

С не-generic предположим, что тип имеет конструктор по умолчанию и catch исключение, если это не так.

Public Function CloneObject(ByVal src As Object) As Object
    Dim result As Object = Nothing
    Dim cloneable As ICloneable
    Try
        cloneable = TryCast(src, ICloneable)
        If cloneable IsNot Nothing Then
            result = cloneable.Clone()
        Else
            result = Activator.CreateInstance(src.GetType())
            CopySimpleProperties(src, result, Nothing, "clone")
        End If
    Catch ex As Exception
        Trace.WriteLine("!!! CloneObject(): " & ex.Message)
    End Try
    Return result
End Function
3
public AbstractType New
{
    get
    {
        return (AbstractType) Activator.CreateInstance(GetType());
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню