una qué que programacion objetos objeto metodos metodo instancia ejemplos clases clase c# .net performance reflection types

qué - que es una instancia en c#



Cómo crear una nueva instancia de objeto desde un Tipo (12)

Es posible que no siempre se sepa el Type de un objeto en tiempo de compilación, pero es posible que deba crear una instancia del Type . ¿Cómo se obtiene una nueva instancia de objeto de un Type ?


¿No sería el genérico T t = new T(); ¿trabajo?


Dado este problema, el activador funcionará cuando haya un ctor sin parámetros. Si esto es una restricción, considera usar

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()


Es bastante simple. Suponga que su nombre de clase es Car y el espacio de nombres es Vehicles , luego pase el parámetro como Vehicles.Car que devuelve el objeto de tipo Car . Así puedes crear cualquier instancia de cualquier clase dinámicamente.

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

Si su nombre completo (es decir, Vehicles.Car en este caso) está en otro ensamblaje, el Type.GetType será nulo. En tales casos, tiene que recorrer todos los ensamblajes y encontrar el Type . Para eso puedes usar el siguiente código

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

Y puede obtener la instancia llamando al método anterior.

object objClassInstance = GetInstance("Vehicles.Car");


Expresión compilada es la mejor manera! (para que el rendimiento cree repetidamente la instancia en tiempo de ejecución).

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

Estadísticas (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

Estadísticas (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

Estadísticas (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

Estadísticas (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

Estadísticas (2019, x64 / .NET 4.8):

Iterations: 5000000 No args 00:00:00.3287835, Activator.CreateInstance(string assemblyName, string typeName) 00:00:00.3122015, Activator.CreateInstance(Type type) 00:00:00.8035712, ConstructorInfo.Invoke 00:00:00.0692854, Compiled expression 00:00:00.0662223, Compiled expression (type) 00:00:00.0337862, new Single arg 00:00:03.8081959, Activator.CreateInstance(Type type) 00:00:01.2507642, ConstructorInfo.Invoke 00:00:00.0671756, Compiled expression 00:00:00.0301489, new

Estadísticas (2019, x64 / .NET Core 3.0):

Iterations: 5000000 No args 00:00:00.3226895, Activator.CreateInstance(string assemblyName, string typeName) 00:00:00.2786803, Activator.CreateInstance(Type type) 00:00:00.6183554, ConstructorInfo.Invoke 00:00:00.0483217, Compiled expression 00:00:00.0485119, Compiled expression (type) 00:00:00.0434534, new Single arg 00:00:03.4389401, Activator.CreateInstance(Type type) 00:00:01.0803609, ConstructorInfo.Invoke 00:00:00.0554756, Compiled expression 00:00:00.0462232, new

Código completo:

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


La clase Activator dentro del espacio de nombres del System raíz es bastante poderosa.

Hay una gran cantidad de sobrecargas para pasar parámetros al constructor y demás. Revisa la documentación en:

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

o (nuevo camino)

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

Aquí hay algunos ejemplos simples:

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType); ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");


Puedo responder esta pregunta porque estaba buscando implementar un método CloneObject simple para una clase arbitraria (con un constructor predeterminado)

Con el método genérico puede requerir que el tipo implemente 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

Con el método no genérico, supongamos que el tipo tiene un constructor predeterminado y detecta una excepción si no lo tiene.

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


Si desea utilizar el constructor predeterminado, la solución que use System.Activator presentada anteriormente es probablemente la más conveniente. Sin embargo, si el tipo carece de un constructor predeterminado o si tiene que usar uno que no sea el predeterminado, entonces una opción es usar la reflexión o System.ComponentModel.TypeDescriptor . En caso de reflexión, basta con saber el nombre del tipo (con su espacio de nombres).

Ejemplo utilizando la reflexión:

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

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


Si esto es para algo que se llamará mucho en una instancia de aplicación, es mucho más rápido compilar y almacenar en caché el código dinámico en lugar de usar el activador o ConstructorInfo.Invoke() . Dos opciones fáciles para la compilación dinámica son las Expresiones de Linq compiladas o algunos DynamicMethod simples de IL y el DynamicMethod . De cualquier manera, la diferencia es enorme cuando comienzas a entrar en bucles cerrados o múltiples llamadas.


Sin uso de la reflexión:

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


Una implementación de este problema es intentar llamar al constructor sin parámetros del tipo:

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

Aquí está el mismo enfoque, contenido en un método genérico:

public static T GetNewObject<T>() { try { return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { }); } catch { return default(T); } }


public AbstractType New { get { return (AbstractType) Activator.CreateInstance(GetType()); } }


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

La clase Activator tiene una variante genérica que facilita esto un poco:

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