Создание экземпляра типа без конструктора по умолчанию в С# с использованием отражения
В качестве примера возьмем следующий класс:
class Sometype
{
int someValue;
public Sometype(int someValue)
{
this.someValue = someValue;
}
}
Затем я хочу создать экземпляр этого типа с помощью отражения:
Type t = typeof(Sometype);
object o = Activator.CreateInstance(t);
Обычно это будет работать, однако, поскольку SomeType
не определил конструктор без параметров, вызов Activator.CreateInstance
вызовет исключение типа MissingMethodException
с сообщением "Без конструктора без параметров, определенного для этого объекта". Есть ли альтернативный способ создания экземпляра этого типа? Было бы неплохо добавить конструкторы без параметров для всех моих классов.
Ответы
Ответ 1
Я изначально разместил этот ответ здесь, но вот перепечатка, так как это не тот самый вопрос, но имеет тот же ответ:
FormatterServices.GetUninitializedObject()
создаст экземпляр без вызова конструктора. Я нашел этот класс, используя Reflector и пробив некоторые из основных классов сериализации .Net.
Я тестировал его, используя пример кода ниже, и похоже, что он отлично работает:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;
namespace NoConstructorThingy
{
class Program
{
static void Main(string[] args)
{
MyClass myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass)); //does not call ctor
myClass.One = 1;
Console.WriteLine(myClass.One); //write "1"
Console.ReadKey();
}
}
public class MyClass
{
public MyClass()
{
Console.WriteLine("MyClass ctor called.");
}
public int One
{
get;
set;
}
}
}
Ответ 2
Используйте эту перегрузку метода CreateInstance:
public static Object CreateInstance(
Type type,
params Object[] args
)
Создает экземпляр указанного типа, используя конструктор, который наилучшим образом соответствует указанным параметрам.
Смотрите: http://msdn.microsoft.com/en-us/library/wcxyzt4d.aspx
Ответ 3
Когда я сравнивал производительность (T)FormatterServices.GetUninitializedObject(typeof(T))
, он был медленнее. В то же время скомпилированные выражения дадут вам большие улучшения скорости, хотя они работают только для типов с конструктором по умолчанию. Я взял гибридный подход:
public static class New<T>
{
public static readonly Func<T> Instance = Creator();
static Func<T> Creator()
{
Type t = typeof(T);
if (t == typeof(string))
return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();
if (t.HasDefaultConstructor())
return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();
return () => (T)FormatterServices.GetUninitializedObject(t);
}
}
public static bool HasDefaultConstructor(this Type t)
{
return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}
Это означает, что выражение create эффективно кэшируется и берет на себя штраф только в первый раз, когда тип загружается. Эффективно будет обрабатывать типы значений.
Назовите его:
MyType me = New<MyType>.Instance();
Обратите внимание, что (T)FormatterServices.GetUninitializedObject(t)
не будет выполнено для строки. Следовательно, для обработки пустой строки требуется специальная обработка строки.
Ответ 4
Хорошие ответы, но непригодные для использования в чистых компактных рамках. Вот решение, которое будет работать на CF.Net...
class Test
{
int _myInt;
public Test(int myInt)
{
_myInt = myInt;
}
public override string ToString()
{
return "My int = " + _myInt.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var ctor = typeof(Test).GetConstructor(new Type[] { typeof(int) });
var obj = ctor.Invoke(new object[] { 10 });
Console.WriteLine(obj);
}
}