Получение нового экземпляра объекта из типа



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

695   12  

12 ответов:

The 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");
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);

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

ObjectType instance = Activator.CreateInstance<ObjectType>();

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

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

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

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

это довольно просто. Предположим, что ваше имя это 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");

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

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

private T Create<T>() where T : class, new()
{
    return new T();
}

если вы хотите использовать конструктор по умолчанию, то решение с помощью System.Activator представленный ранее, вероятно, самый удобный. Однако, если в типе отсутствует конструктор по умолчанию или вы должны использовать нестандартный, то можно использовать reflection или 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 }
    );

не было бы общего T t = new T(); работы?

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

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()

Я могу ответить на этот вопрос, потому что я хотел реализовать простой метод 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

С non-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
public AbstractType New
{
    get
    {
        return (AbstractType) Activator.CreateInstance(GetType());
    }
}

Comments

    Ничего не найдено.