Создание экземпляра класса с внутренним конструктором

У меня есть класс, конструктор которого определен как внутренний, а это значит, что я не могу его создать. Хотя это может иметь смысл, я все же хотел бы сделать это один раз для отладки и исследовательских целей.

Можно ли сделать это с помощью Reflection? Я знаю, что могу получить доступ к частным / внутренним членам, но могу ли я вызвать внутренний конструктор?

Или, поскольку конструктор не делает ничего важного, могу ли я использовать отражение, чтобы сказать: «Послушайте, просто дайте мне экземпляр класса, не вызывая конструктор, я сделаю это вручную»?

Производительность и «стабильность» здесь не проблема, так как это не производственный код.

Изменить: в качестве пояснения: к сожалению, я не контролирую другую сборку и не имею ее исходного кода, я просто пытаюсь понять, как она работает, поскольку ее документации почти не существует, но я должен взаимодействовать с этим.


person Michael Stum    schedule 29.07.2009    source источник


Ответы (9)


Существует метод FormatterServices.GetUninitializedObject ( Namespace: System.Runtime.Serialization), он якобы не вызывает конструкторов, если вы действительно хотите опробовать этот подход.

person Kenan E. K.    schedule 29.07.2009
comment
Это и Type.GetField () / FieldInfo.SetValue решили мою проблему. - person Michael Stum; 29.07.2009
comment
@Kenan: рискованно, если конструктор принимает аргументы и работает с этими аргументами, лучше использовать отражение и вызывать его, а не создавать единичный объект и реплицировать поведение конструктора из экземпляра за пределами с помощью отражения. Мне кажется, что решение Tore лучше. - person Skary; 27.12.2020

В качестве альтернативы можно было бы назначить вызывающую сборку как «дружественную» сборку.

Просто добавьте это в файл AssemblyInfo.cs сборки, содержащей внутренний конструктор:

[assembly: InternalsVisibleTo("Calling.Assembly")]

Если у вас нет доступа к сборке, вы также можете вызвать конструктор напрямую (используя Reflection):

MyClass obj = (MyClass) typeof(MyClass).GetConstructor(
                  BindingFlags.NonPublic | BindingFlags.Instance,
                  null, Type.EmptyTypes, null).Invoke(null);
person Philippe Leybaert    schedule 29.07.2009
comment
К сожалению, я не контролирую другую сборку :( - person Michael Stum; 29.07.2009
comment
Отредактировал свой ответ, чтобы включить решение с использованием отражения - person Philippe Leybaert; 29.07.2009
comment
По какой-то причине GetContructor возвращает null даже при экспериментировании с BindingFlags. Но в целом это полезный ответ, так что +1 к этому. - person Michael Stum; 29.07.2009

Это метод, заимствованный из этого ответа:

public static T CreateInstance<T>(params object[] args)
{
    var type = typeof (T);
    var instance = type.Assembly.CreateInstance(
        type.FullName, false,
        BindingFlags.Instance | BindingFlags.NonPublic,
        null, args, null, null);
    return (T) instance;
}

Пример использования (это тип SDK Kinect, который мне нужно было создать для модульных тестов):

DiscreteGestureResult a = CreateInstance<DiscreteGestureResult>(false, false, 0.5f);
person weston    schedule 09.06.2015

Некоторое время назад я столкнулся с такой же ситуацией и создал небольшую утилиту, которую назвал «InternalsVisibleToInjector». Он использует ILDASM и ILASM для дизассемблирования, изменения, повторной сборки и сборки с добавлением имени сборки по моему выбору в список InternalsVisibleTo для целевой сборки. В моей ситуации это сработало.

Я разместил исходный код (VS 2008 C # WinForm) для утилиты здесь:

http://www.schematrix.com/downloads/InternalsVisibleToInjector.zip

Это может не сработать, если сборка подписана. Пожалуйста, примите все необходимые меры предосторожности (т.е. сделайте резервную копию сборки перед ее использованием и убедитесь, что у вас есть твердые юридические основания и т. Д.)

person Michael A. McCloskey    schedule 29.07.2009

Если вы хотите избежать отражения, вы можете использовать выражения. Вот пример вызова частного конструктора со строковым значением.

    private static Func<string, T> CreateInstanceFunc()
    {
        var flags = BindingFlags.NonPublic | BindingFlags.Instance;
        var ctor = typeof(T).GetConstructors(flags).Single(
            ctors =>
            {
                var parameters = ctors.GetParameters();
                return parameters.Length == 1 && parameters[0].ParameterType == typeof(string);
            });
        var value = Expression.Parameter(typeof(string), "value");
        var body = Expression.New(ctor, value);
        var lambda = Expression.Lambda<Func<string, T>>(body, value);

        return lambda.Compile();
    }
person Xavier John    schedule 24.03.2017
comment
Как это избежать отражения? typeof(T).GetConstructors(flags). - person weston; 18.04.2017

Вот более практический пример. Я хочу создать экземпляр ObjectMaterializedEventArg из Entity Framework. Выглядит это так:

namespace System.Data.Entity.Core.Objects
{
    /// <summary>EventArgs for the ObjectMaterialized event.</summary>
    public class ObjectMaterializedEventArgs : EventArgs
    {
        private readonly object _entity;

        internal ObjectMaterializedEventArgs(object entity)
        {
            this._entity = entity;
        }

        /// <summary>Gets the entity object that was created.</summary>
        /// <returns>The entity object that was created.</returns>
        public object Entity
        {
            get { return this._entity; }
        }
    }
}

Как мы видим, это событие arg использует только внутренний конструктор.

Чтобы выполнить модульное тестирование правила расшифровки пациента в программной системе, с которой я работаю, нам нужно создать экземпляр такого объекта, поэтому в итоге я использовал вместо этого метод GetConstructors.

[Test]
public void EmptyNameAndOfficialIdDoesNotThrow()
{
    var patientDecryptingRule = new PatientDecryptingRule();
    object[] reservation = new object[]
    {
        new Operation
        {
            Status = (int) OperationStatusDataContract.Reservation,
            Patient = new Patient
            {
                Name = null,
                OfficialId = null,
                IsPatientEncrypted = true
            }
        }
    };

    var relevantConstructor = typeof(ObjectMaterializedEventArgs).GetConstructors(
        BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();

    ObjectMaterializedEventArgs objectMaterializedEventArgs =
        (ObjectMaterializedEventArgs) relevantConstructor?.Invoke(reservation);

    patientDecryptingRule.ModifyObjectMaterialized(objectMaterializedEventArgs);
}

Я использую здесь GetConstructors, указывая, что конструктор, который нужно искать, не является общедоступным (например, внутренний) и экземпляр как bindingflags, затем используйте FirsOrDefault.

Надеюсь, этот сценарий будет полезен тем, у кого есть проблемы с GetConstructor правильным ответом. Вместо этого вы можете использовать GetConstructors и при необходимости отфильтровать еще несколько. Затем в ?.Invoke() передайте массив объектов ваших параметров.

person Tore Aurstad    schedule 22.08.2019

Вы можете использовать Reflector, чтобы проанализировать его исходный код и, исходя из этого, выяснить внутреннюю работу. .

person Peter Lillevold    schedule 29.07.2009
comment
@AdamPlocher - развлекайтесь. В ОП четко сказано, что я просто пытаюсь понять, как это работает. Обратный инжиниринг кода из IL, imho, очень жизнеспособный способ понимания кода. Я бы сказал, что для понимания внутренней работы какой-либо сборки легче читать код, чем пытаться вытолкнуть экземпляр во время выполнения. Итак, мой ответ остается в силе, независимо от того, проголосовали против него или нет. - person Peter Lillevold; 25.07.2014
comment
И @AdamPlocher: моему 5-летнему ответу столько же лет, сколько и всем остальным ответам на этот вопрос. Эй, даже вопрос 5 лет! Так что с этим? - person Peter Lillevold; 25.07.2014

Если кто-то снова наткнется на это, вот пример того, как поднять его с помощью отражения:

var args = FormatterServices.GetUninitializedObject(typeof(SizeChangedEventArgs)) as SizeChangedEventArgs;
Debug.Assert(args != null);

var field = typeof(SizeChangedEventArgs).GetField("_previousSize", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, new Size(0,0));
field = typeof(SizeChangedEventArgs).GetField("_element", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, GraphicsWrapper);
field = typeof(RoutedEventArgs).GetField("_source", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, GraphicsWrapper);
field = typeof(RoutedEventArgs).GetField("_routedEvent", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(field != null);
field.SetValue(args, SizeChangedEvent);

GraphicsWrapper.RaiseEvent(args);

... где GraphicsWrapper - это элемент управления WPF, из которого вы хотите его поднять.

person outbred    schedule 03.03.2017
comment
Б / ц ... внутренним ctor вызвать нельзя. Отсюда и приведенный выше пример. Используя метод FormatterServices.GetUninitializedObject () в ПРИНЯТОМ ответе, вы обойдете это ограничение. - person outbred; 05.03.2017
comment
Извините, я не обратил внимания. +1 - person Martin Prikryl; 05.03.2017
comment
Я благодарен за повторный визит, @MartinPrikryl. Спасибо! - person outbred; 10.03.2017

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

В целях тестирования вы также можете разрешить своей тестовой сборке доступ к внутренним компонентам, используя атрибут InternalsVisibleTo. См. http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx

person Brian Rasmussen    schedule 29.07.2009
comment
Э ... что, если вы хотите создать его экземпляр из другой сборки. Тогда ты не сможешь .... - person cjk; 29.07.2009
comment
Правильно, если вы не можете использовать InternalsVisibleTo. С обновлением вопроса, похоже, это не так, но это было неясно для меня из исходной формулировки. - person Brian Rasmussen; 29.07.2009