Подтвердить что ты не робот

Вычисление класса с внутренним конструктором

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

Альтернативой было бы назначить вызывающую сборку как сборку "друг".

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

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

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

MyClass obj = (MyClass) typeof(MyClass).GetConstructor(
                  BindingFlags.NonPublic | BindingFlags.Instance,
                  null, Type.EmptyTypes, null).Invoke(null);

Ответ 3

Это метод, полученный из этого ответа:

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;
}

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

DiscreteGestureResult a = CreateInstance<DiscreteGestureResult>(false, false, 0.5f);

Ответ 4

Я пережил эту же ситуацию некоторое время назад и создал небольшую утилиту, которую я назвал "InternalsVisibleToInjector". Он использует ILDASM и ILASM для демонтажа, модификации и сборки и сборки с именем сборки моего выбора, добавленным в список InternalsVisibleTo для целевой сборки. В моей ситуации он работал очень хорошо.

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

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

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

Ответ 5

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

    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();
    }

Ответ 6

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

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, с которым вы хотите его повысить.

Ответ 7

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

namespace System.Data.Entity.Core.Objects

{ ///EventArgs для события ObjectMaterialized. открытый класс ObjectMaterializedEventArgs: EventArgs {   закрытый объект только для чтения _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;
  }
}

} }

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

Чтобы выполнить модульное тестирование для правила расшифровки пациента в программной системе, с которой я работаю, нам нужно создать экземпляр такого объекта, поэтому я вместо этого использовал метод 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, определяя, что конструкторы, которые нужно искать, не являются публичными (например, внутренними) и экземпляром в качестве флагов привязки, а затем используем FirsOrDefault.

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

Ответ 8

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

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

Ответ 9

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