หนึ่งอาจจะไม่เคยรู้Type
ของวัตถุที่รวบรวมเวลา Type
แต่อาจจำเป็นต้องสร้างอินสแตนซ์ที่
คุณจะรับอินสแตนซ์ของวัตถุใหม่ได้Type
อย่างไร
หนึ่งอาจจะไม่เคยรู้Type
ของวัตถุที่รวบรวมเวลา Type
แต่อาจจำเป็นต้องสร้างอินสแตนซ์ที่
คุณจะรับอินสแตนซ์ของวัตถุใหม่ได้Type
อย่างไร
คำตอบ:
Activator
ระดับภายในรากSystem
namespace มีประสิทธิภาพสวย
มีจำนวนมากเกินพิกัดสำหรับการส่งพารามิเตอร์ไปยังตัวสร้างและเช่น ตรวจสอบเอกสารที่:
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
ตรงกับสภาพของ OP "หนึ่งอาจไม่ทราบชนิดของวัตถุที่รวบรวมเวลา" หรือไม่? : P
object instance = Activator.CreateInstance(...);
โอเคแล้ว
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);
Activator
ชั้นจะมีตัวแปรทั่วไปที่ทำให้เรื่องนี้ง่ายขึ้นเล็กน้อย:
ObjectType instance = Activator.CreateInstance<ObjectType>();
dynamic
โครงสร้างซึ่งจะช่วยให้โครงสร้างดังกล่าว แต่สำหรับวัตถุประสงค์มากที่สุดคำตอบนี้ยังครอบคลุม
รวบรวมการแสดงออกเป็นวิธีที่ดีที่สุด! (เพื่อประสิทธิภาพในการสร้างอินสแตนซ์ในรันไทม์ซ้ำ ๆ )
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
สถิติ (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
สถิติ (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
รหัสเต็ม:
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) {}
}
X
รันไทม์หรือไม่
Type
เป็นรันไทม์
การดำเนินการอย่างหนึ่งของปัญหานี้คือการพยายามเรียกตัวสร้างพารามิเตอร์ที่ไม่ใช้ประเภท:
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);
}
}
มันค่อนข้างง่าย สมมติว่า ClassName ของคุณCar
และ namespace จะVehicles
แล้วผ่านพารามิเตอร์ซึ่งผลตอบแทนวัตถุประเภทVehicles.Car
Car
เช่นนี้คุณสามารถสร้างตัวอย่างของคลาสใดก็ได้แบบไดนามิก
public object GetInstance(string strNamesapace)
{
Type t = Type.GetType(strNamesapace);
return Activator.CreateInstance(t);
}
หากชื่อที่ผ่านการรับรองของคุณ(เช่นVehicles.Car
ในกรณีนี้) อยู่ในการชุมนุมอื่นType.GetType
จะเป็นโมฆะ 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");
หากนี่เป็นสิ่งที่จะถูกเรียกว่าเป็นจำนวนมากในกรณีโปรแกรมมันเร็วมากเพื่อรวบรวมและรหัสแคชแบบไดนามิกแทนการใช้ Activator ConstructorInfo.Invoke()
หรือ สองตัวเลือกที่ง่ายสำหรับการรวบรวมแบบไดนามิกเป็นข้อมูลที่รวบรวมLinq นิพจน์หรือบางอย่างง่ายIL
opcodes DynamicMethod
และ ความแตกต่างนั้นมีขนาดใหญ่มากเมื่อคุณเริ่มมีปัญหากับลูปหรือการโทรหลายสาย
จะไม่T t = new T();
ทำงานทั่วไปหรือไม่
หากคุณต้องการใช้คอนสตรัคเตอร์เริ่มต้นวิธีแก้ปัญหาที่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 }
);
args[]
เป็นสิ่งที่ฉันมาถึงคำถามนี้เพื่อค้นหาขอบคุณ!
โดยไม่ต้องใช้ Reflection:
private T Create<T>() where T : class, new()
{
return new T();
}
เมื่อได้รับปัญหานี้ Activator จะทำงานเมื่อไม่มี ctor ที่ไม่มีพารามิเตอร์ หากนี่เป็นข้อ จำกัด ให้พิจารณาใช้
System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject()
public AbstractType New
{
get
{
return (AbstractType) Activator.CreateInstance(GetType());
}
}
ฉันสามารถข้ามคำถามนี้เพราะฉันต้องการใช้วิธีการ CloneObject ง่าย ๆ สำหรับชั้นเรียนโดยพลการ (กับตัวสร้างเริ่มต้น)
ด้วยวิธีการทั่วไปคุณสามารถกำหนดให้ประเภทดำเนินการใหม่ ()
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
หากไม่ใช่แบบทั่วไปสมมติว่าชนิดนั้นมีตัวสร้างค่าเริ่มต้นและตรวจจับข้อยกเว้นหากไม่มี
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