Non si può sempre conoscere il tipo di un oggetto al momento della compilazione, ma potrebbe essere necessario creare un'istanza del tipo. Come si ottiene una nuova istanza di oggetto da un tipo?
Ottenere una nuova istanza di oggetto da un tipo
Un'implementazione di questo problema è quello di tentare di chiamare il costruttore senza parametri del tipo:
public static object GetNewObject(Type t)
{
try
{
return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
}
catch
{
return null;
}
}
Ecco lo stesso approccio, contenuto in un metodo generico:
public static T GetNewObject<T>()
{
try
{
return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { });
}
catch
{
return default(T);
}
}

La Activator
classe all'interno della radice System
dello spazio dei nomi è abbastanza potente.
Ci sono un sacco di sovraccarichi per passare parametri al costruttore e così via. Controlla la documentazione all'indirizzo:
http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx
o (nuovo percorso)
https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance
Ecco alcuni semplici esempi:
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);
ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType");
Se questo è per qualcosa che sarà chiamato un sacco in un'istanza di applicazione, è molto più veloce per compilare e codice dinamico della cache invece di utilizzare l'attivatore o ConstructorInfo.Invoke()
. Due semplici opzioni per la compilazione dinamica vengono compilati Linq espressioni o alcuni semplici IL
codici operativi eDynamicMethod
. In entrambi i casi, la differenza è enorme quando si inizia a entrare in cicli stretti o chiamate multiple.
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);
La Activator
classe ha una variante generica che rende questo un po 'più facile:
ObjectType instance = Activator.CreateInstance<ObjectType>();
Non sarebbe il generico T t = new T();
lavoro?
public AbstractType New
{
get
{
return (AbstractType) Activator.CreateInstance(GetType());
}
}
Se si desidera utilizzare il costruttore di default, allora la soluzione con System.Activator
presentato in precedenza è probabilmente il più conveniente. Tuttavia, se il tipo manca un costruttore di default o si deve utilizzare uno non predefinito, quindi una possibilità è quella di utilizzare la riflessione o System.ComponentModel.TypeDescriptor
. In caso di riflessione, è sufficiente sapere solo il nome del tipo (con il suo spazio dei nomi).
Esempio con riflessione:
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
);
Esempio con 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 }
);
La sua piuttosto semplice. Si supponga che il nome classe è Car
e lo spazio dei nomi è Vehicles
, quindi passare il parametro come Vehicles.Car
che restituisce oggetto di tipo Car
. Ti piace questa è possibile creare qualsiasi istanza di qualsiasi classe in modo dinamico.
public object GetInstance(string strNamesapace)
{
Type t = Type.GetType(strNamesapace);
return Activator.CreateInstance(t);
}
Se il vostro nome Fully Qualified (vale a dire, Vehicles.Car
in questo caso) è in un altro assembly, il Type.GetType
sarà nullo. In questi casi, si deve scorrere tutti i gruppi e trovare il Type
. Per questo è possibile utilizzare il codice qui sotto
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;
}
E si può ottenere l'istanza chiamando il metodo di cui sopra.
object objClassInstance = GetInstance("Vehicles.Car");
Posso in tutta questa domanda perché stavo cercando di implementare un metodo semplice per la classe CLONEOBJECT arbitraria (con un costruttore di default)
Con metodo generico si può esigere che il tipo implementa Nuovo ().
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 non generico assumere il tipo ha un costruttore di default e prendere un'eccezione se non è così.
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
espressione compilato è modo migliore! (Per le prestazioni a volte creare l'istanza in fase di esecuzione).
static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
X x = YCreator();
Statistiche (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
Statistiche (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
Statistiche (2015, .net 4.5, 86):
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
Statistiche (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
codice 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) {}
}
Senza l'uso di riflessione:
private T Create<T>() where T : class, new()
{
return new T();
}
Dato questo problema l'Activator funziona quando c'è una ctor senza parametri. Se questo è un vincolo considerare l'utilizzo
System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()