建立 Type 的例項
最簡單的方法是使用 Activator
類。
然而,儘管自 .NET 3.5 以來 Activator
效能得到了提升,但由於(相對)低效能,使用 Activator.CreateInstance()
有時候選擇不好: 測試 1 ,測試 2 ,測試 3 ……
用 Activator
級
Type type = typeof(BigInteger);
object result = Activator.CreateInstance(type); //Requires parameterless constructor.
Console.WriteLine(result); //Output: 0
result = Activator.CreateInstance(type, 123); //Requires a constructor which can receive an 'int' compatible argument.
Console.WriteLine(result); //Output: 123
如果你有多個引數,則可以將物件陣列傳遞給 Activator.CreateInstance
。
// With a constructor such as MyClass(int, int, string)
Activator.CreateInstance(typeof(MyClass), new object[] { 1, 2, "Hello World" });
Type type = typeof(someObject);
var instance = Activator.CreateInstance(type);
對於通用型別
MakeGenericType
方法通過對型別引數應用型別引數,將開放泛型型別(如 List<>
)轉換為具體型別(如 List<string>
)。
// generic List with no parameters
Type openType = typeof(List<>);
// To create a List<string>
Type[] tArgs = { typeof(string) };
Type target = openType.MakeGenericType(tArgs);
// Create an instance - Activator.CreateInstance will call the default constructor.
// This is equivalent to calling new List<string>().
List<string> result = (List<string>)Activator.CreateInstance(target);
在 typeof
表示式之外不允許使用 List<>
語法。
沒有 Activator
級
使用 new
關鍵字(將為無引數建構函式執行)
T GetInstance<T>() where T : new()
{
T instance = new T();
return instance;
}
使用 Invoke 方法
// Get the instance of the desired constructor (here it takes a string as a parameter).
ConstructorInfo c = typeof(T).GetConstructor(new[] { typeof(string) });
// Don't forget to check if such constructor exists
if (c == null)
throw new InvalidOperationException(string.Format("A constructor for type '{0}' was not found.", typeof(T)));
T instance = (T)c.Invoke(new object[] { "test" });
使用表示式樹
表示式樹表示樹狀資料結構中的程式碼,其中每個節點都是表示式。正如 MSDN 解釋的那樣:
表示式是一個或多個運算元以及零個或多個運算子的序列,可以將其計算為單個值,物件,方法或名稱空間。表示式可以包含文字值,方法呼叫,運算子及其運算元,或簡單名稱。簡單名稱可以是變數的名稱,型別成員,方法引數,名稱空間或型別。
public class GenericFactory<TKey, TType>
{
private readonly Dictionary<TKey, Func<object[], TType>> _registeredTypes; // dictionary, that holds constructor functions.
private object _locker = new object(); // object for locking dictionary, to guarantee thread safety
public GenericFactory()
{
_registeredTypes = new Dictionary<TKey, Func<object[], TType>>();
}
/// <summary>
/// Find and register suitable constructor for type
/// </summary>
/// <typeparam name="TType"></typeparam>
/// <param name="key">Key for this constructor</param>
/// <param name="parameters">Parameters</param>
public void Register(TKey key, params Type[] parameters)
{
ConstructorInfo ci = typeof(TType).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.HasThis, parameters, new ParameterModifier[] { }); // Get the instance of ctor.
if (ci == null)
throw new InvalidOperationException(string.Format("Constructor for type '{0}' was not found.", typeof(TType)));
Func<object[], TType> ctor;
lock (_locker)
{
if (!_registeredTypes.TryGetValue(key, out ctor)) // check if such ctor already been registered
{
var pExp = Expression.Parameter(typeof(object[]), "arguments"); // create parameter Expression
var ctorParams = ci.GetParameters(); // get parameter info from constructor
var argExpressions = new Expression[ctorParams.Length]; // array that will contains parameter expessions
for (var i = 0; i < parameters.Length; i++)
{
var indexedAcccess = Expression.ArrayIndex(pExp, Expression.Constant(i));
if (!parameters[i].IsClass && !parameters[i].IsInterface) // check if parameter is a value type
{
var localVariable = Expression.Variable(parameters[i], "localVariable"); // if so - we should create local variable that will store paraameter value
var block = Expression.Block(new[] { localVariable },
Expression.IfThenElse(Expression.Equal(indexedAcccess, Expression.Constant(null)),
Expression.Assign(localVariable, Expression.Default(parameters[i])),
Expression.Assign(localVariable, Expression.Convert(indexedAcccess, parameters[i]))
),
localVariable
);
argExpressions[i] = block;
}
else
argExpressions[i] = Expression.Convert(indexedAcccess, parameters[i]);
}
var newExpr = Expression.New(ci, argExpressions); // create expression that represents call to specified ctor with the specified arguments.
_registeredTypes.Add(key, Expression.Lambda(newExpr, new[] { pExp }).Compile() as Func<object[], TType>); // compile expression to create delegate, and add fucntion to dictionary
}
}
}
/// <summary>
/// Returns instance of registered type by key.
/// </summary>
/// <typeparam name="TType"></typeparam>
/// <param name="key"></param>
/// <param name="args"></param>
/// <returns></returns>
public TType Create(TKey key, params object[] args)
{
Func<object[], TType> foo;
if (_registeredTypes.TryGetValue(key, out foo))
{
return (TType)foo(args);
}
throw new ArgumentException("No type registered for this key.");
}
}
可以像這樣使用:
public class TestClass
{
public TestClass(string parameter)
{
Console.Write(parameter);
}
}
public void TestMethod()
{
var factory = new GenericFactory<string, TestClass>();
factory.Register("key", typeof(string));
TestClass newInstance = factory.Create("key", "testParameter");
}
使用 FormatterServices.GetUninitializedObject
T instance = (T)FormatterServices.GetUninitializedObject(typeof(T));
如果使用 FormatterServices.GetUninitializedObject
建構函式,則不會呼叫欄位初始值設定項。它旨在用於序列器和遠端引擎