유형에서 새 객체 인스턴스를 만드는 방법
-
08-06-2019 - |
문제
항상 알 수는 없을 수도 있지만 Type
컴파일 타임에 객체의 인스턴스를 생성해야 할 수도 있습니다. Type
.새 객체 인스턴스를 어떻게 얻나요? Type
?
해결책
그만큼 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);
그만큼 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
통계(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) {}
}
이 문제의 한 가지 구현은 매개변수가 없는 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);
}
}
꽤 간단합니다.귀하의 클래스 이름이 다음과 같다고 가정하십시오. 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
opcode 및 DynamicMethod
.어느 쪽이든, 긴밀한 루프나 여러 호출이 시작되면 차이가 엄청납니다.
반사를 사용하지 않고:
private T Create<T>() where T : class, new()
{
return new T();
}
일반은 안되나요 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 }
);
이 문제가 발생하면 Activator는 매개변수가 없는 ctor가 있을 때 작동합니다.이것이 제약 조건인 경우 다음을 사용하는 것을 고려하십시오.
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
비 게 릭을 사용하면 유형에 기본 생성자가 있고 그렇지 않은 경우 예외를 포착합니다.
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());
}
}