Создание экземпляра типа без конструктора по умолчанию в C # с использованием отражения

В качестве примера возьмем следующий класс:

class Sometype
{
    int someValue;

    public Sometype(int someValue)
    {
        this.someValue = someValue;
    }
}

Затем я хочу создать экземпляр этого типа, используя отражение:

Type t = typeof(Sometype);
object o = Activator.CreateInstance(t);

Обычно это работает, однако, поскольку SomeType не определил конструктор без параметров, вызов Activator.CreateInstance вызовет исключение типа MissingMethodException с сообщением «Для этого объекта не определен конструктор без параметров.» Есть ли альтернатива способ еще создать экземпляр этого типа? Было бы отстойно добавлять конструкторы без параметров ко всем моим классам.


person Aistina    schedule 24.12.2008    source источник
comment
FormatterServices.GetUninitializedObject не разрешать создание неинициализированной строки. Вы можете получить исключение: System.ArgumentException: Uninitialized Strings cannot be created. Имейте это в виду.   -  person Bartosz Pierzchlewicz    schedule 15.01.2010
comment
Спасибо за внимание, но я уже обрабатываю строки и базовые типы отдельно.   -  person Aistina    schedule 15.01.2010


Ответы (4)


Первоначально я разместил этот ответ здесь , но вот перепечатка, поскольку это не тот же вопрос, но имеет тот же ответ:

FormatterServices.GetUninitializedObject() создаст экземпляр без вызова конструктора. Я нашел этот класс, используя Reflector и покопавшись в некоторой части ядра. Сетевые классы сериализации.

Я протестировал его, используя приведенный ниже пример кода, и похоже, что он отлично работает:

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;
        }
    }
}
person Jason Jackson    schedule 24.12.2008
comment
Отлично, похоже, это именно то, что мне нужно. Я предполагаю, что неинициализированный означает, что вся его память будет обнулена? (Подобно тому, как создаются экземпляры структур) - person Aistina; 24.12.2008
comment
Значение по умолчанию для каждого типа будет значением по умолчанию. Таким образом, объекты будут иметь значение null, int 0 и т. Д. Я думаю, что любая инициализация на уровне класса происходит, но конструктор не запускается. - person Jason Jackson; 24.12.2008
comment
@JSBangs, отстой, вы даете вполне законный ответ. Ваш комментарий и другой ответ на самом деле не затрагивают заданный вопрос. Если вы чувствуете, что у вас есть лучший ответ, дайте его. Но предоставленный мной ответ подчеркивает, как использовать документированный класс так же, как другие классы сериализации используют этот код. - person Jason Jackson; 21.11.2010
comment
@JSBangs FormatterServices (msdn.microsoft.com/en- us / library /) не является недокументированным. - person Autodidact; 28.12.2010
comment
@Cal - Ага, это очень мощная функция, и ее следует использовать с осторожностью. Я только когда-либо использовал эту функцию в одном месте своего кода за последние 3 года с тех пор, как я опубликовал это. - person Jason Jackson; 27.10.2011
comment
Как я могу вызвать все конструкторы по умолчанию (и базу для унаследованных) позже? - person Wobbles; 23.11.2019

Используйте эту перегрузку метода CreateInstance:

public static Object CreateInstance(
    Type type,
    params Object[] args
)

Создает экземпляр указанного типа, используя конструктор, который лучше всего соответствует указанным параметрам.

См .: http://msdn.microsoft.com/en-us/library/wcxyzt4d.aspx

person Nick    schedule 24.12.2008
comment
Это решение упрощает проблему. Что делать, если я не знаю свой тип и говорю, что просто создайте объект типа в этой переменной типа? - person kamii; 20.04.2017

Когда я тестировал производительность (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) не удастся для строки. Следовательно, для возврата пустой строки применяется специальная обработка строки.

person nawfal    schedule 23.04.2013
comment
Странно, как взгляд на одну строчку чьего-то кода может спасти день. Спасибо, сэр! Причины производительности привели меня к вашему посту, и трюк готов :) Классы FormatterServices и Activator неэффективны по сравнению с скомпилированными выражениями, как жаль, что активаторы повсюду. - person jmodrak; 17.11.2013
comment
@nawfal Что касается вашей специальной обработки строки, я знаю, что она не удалась бы для строки без этой специальной обработки, но я просто хочу знать: будет ли она работать для всех других типов? - person Sнаđошƒаӽ; 18.07.2018
comment
@ Sнаđошƒаӽ к сожалению нет. Данный пример является базовым, и .NET имеет много разных типов типов. Например, подумайте, если вы передадите тип делегата, как вы предоставите ему экземпляр? Или иначе, если конструктор выдает, что вы можете с этим поделать? Много разных способов справиться с этим. С тех пор я ответил на это обновление, чтобы обрабатывать гораздо больше сценариев в моей библиотеке. Он пока нигде не опубликован. - person nawfal; 18.07.2018

Хорошие ответы, но непригодные для использования на компактной платформе dot net. Вот решение, которое будет работать на 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);
    }
}
person Autodidact    schedule 22.01.2009
comment
Так я бы назвал конструктор, отличный от конструктора по умолчанию. Я не уверен, что когда-нибудь захочу создать объект, вообще не вызывая конструктор. - person Rory MacLeod; 18.02.2010
comment
Если вы пишете собственные сериализаторы, вы можете создать объект без вызова конструкторов. - person Autodidact; 21.02.2010
comment
Да, это точный сценарий использования, для которого был задан вопрос :) - person Aistina; 14.05.2010
comment
@Aistina Возможно, вы могли бы добавить эту информацию к вопросу? Большинство людей были бы против создания объектов без обращения к своим ctors и потратили бы время, чтобы поспорить с вами по этому поводу, но ваш вариант использования действительно оправдывает это, поэтому я думаю, что это очень актуально для самого вопроса. - person julealgon; 24.10.2013