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

Возможно ли получить доступ к полям поддержки за автоматически реализованными свойствами?

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

private string _postalCode;

public string PostalCode
{
    get { return _postalCode; }
    set { _postalCode = value; }
}

Или я могу использовать автоматически реализованные свойства.

public string PostalCode { get; set; }

Могу ли я каким-то образом получить доступ к полю резервного копирования, которое находится за авто-реализованным свойством? (В этом примере это будет postalCode).


Изменить. Мой вопрос не о дизайне, а о теоретических возможностях.

4b9b3361

Ответ 1

Я не знаю о вас, но я написал код в проектах в других компаниях, и теперь я хочу знать, как я что-то сделал! Поэтому обычно быстрее выполнять поиск в Интернете для ответа, и это привело меня сюда.

Однако мои причины разные. Я тестирую модуль, и мне все равно, что говорят пуристы, но как часть настройки для unit test, я пытаюсь вызвать определенное состояние для данного объекта. Но это государство должно контролироваться внутри страны. Я не хочу, чтобы какой-то другой разработчик случайно возился с государством, что могло иметь далеко идущие последствия для системы. Поэтому он должен быть конфиденциально установлен! Но как вы unit test что-то вроде этого, не ссылаясь на поведение, которое (надеюсь) никогда не произойдет? В таких сценариях я считаю, что использование отражения с модульным тестированием полезно.

Альтернативой является разоблачение вещей, которые мы не хотим показывать, поэтому мы можем unit test их! Да, я видел это в реальной жизни, и просто думать об этом по-прежнему заставляет меня качать головой.

Итак, я надеюсь, что код ниже может быть полезен.

Здесь есть два метода только для разделения проблем, на самом деле, а также для облегчения читаемости. Отражение - головокружение для большинства разработчиков, которые по моему опыту либо уклоняются от него, либо избегают его, как чуму!

private string _getBackingFieldName(string propertyName)
{
    return string.Format("<{0}>k__BackingField", propertyName);
}

private FieldInfo _getBackingField(object obj, string propertyName)
{
    return obj.GetType().GetField(_getBackingFieldName(propertyName), BindingFlags.Instance | BindingFlags.NonPublic);
}

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

Существует обсуждение полей поддержки и их автоматического присвоения имен. Для целей модульных тестов вы будете знать довольно быстро, если это изменилось или нет! Это не будет катастрофическим для вашего реального кода, просто тесты. Поэтому мы можем сделать простые предположения об именах имен, как я уже говорил выше. Вы можете не согласиться, и это прекрасно.

Чем сложнее вспомогательный _getBackingField возвращает один из этих типов отражения, FieldInfo. Я также сделал предположение, что поле поддержки, которое вы используете, - это объект, который является экземпляром, а не статичным. Вы можете разбить это на аргументы, которые нужно передать, если хотите, но воды наверняка станут более грязными для среднего разработчика, который может захотеть функциональности, но не понимания.

Полезная вещь о FieldInfo заключается в том, что они могут устанавливать поля для объектов, которые соответствуют FieldInfo. Это лучше объясняется с помощью примера:

var field = _getBackingField(myObjectToChange, "State");
field.SetValue(myObjectToChange, ObjectState.Active);

В этом случае поле имеет тип перечисления, называемый ObjectState. Имена были изменены, чтобы защитить невинных! Таким образом, во второй строке вы можете видеть, что, обратившись к FieldInfo, возвращенному ранее, я могу вызвать метод SetValue, который, по вашему мнению, должен уже относиться к вашему объекту, но не делает этого! Это характер отражения. FieldInfo отделяет поле от того места, откуда оно появилось, поэтому вы должны сказать, с каким экземпляром работать (myObjectToChange) и, следовательно, значение, которое вы хотите, в этом случае ObjectState.Active.

Итак, чтобы сделать длинную историю, объектно-ориентированное программирование не позволит нам делать такие неприятные вещи, как доступ к частным полям, и, что еще хуже, изменять их, когда разработчик кода не намеревался. И это хорошо! Эта одна из причин С# настолько ценна и понравилась разработчикам.

Однако Microsoft дала нам Reflection, и благодаря этому мы обладаем могущественным оружием. Он может быть уродливым и очень медленным, но в то же время он раскрывает глубину глубины внутренней работы MSIL (InterSize Language Language) -IL для краткости - и позволяет нам в значительной степени нарушить каждое правило в книге, это являющийся хорошим примером.

Ответ 2

Это происходит прямо из MSDN:

В С# 3.0 и более поздних версиях автоматически реализованные свойства Объявление свойства более кратким, если дополнительная логика не требуется в аксессуарах свойств. Они также позволяют создавать код клиента для создания объекты. Когда вы объявляете свойство, как показано ниже Например, компилятор создает частное, анонимное фоновое поле, которое могут быть доступны только через свойство get и set accessors.

Так что ты не можешь.

Ответ 3

В Visual Studio 2010 по крайней мере вы можете получить список частных полей в классе, используя отражение, если вы явно заявляете, что хотите поля непубличных экземпляров:

FieldInfo[] myInfo = ClassWithPostalCode.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic);

Затем вы можете выполнить цикл через массив FieldInfo. В этом случае вы увидите, что имя поля поддержки, вероятно, будет

<PostalCode> k__BackingField

Я заметил, что все автоматические свойства, похоже, следуют шаблону имени свойства в угловых скобках, за которым следует "k__BackingField", но имейте в виду, что это не является официальным и может измениться в будущих версиях .Net. Я не совсем уверен, что в прошлых версиях это не так.

Как только вы знаете имя поля, вы можете получить его значение таким образом:

object oValue = obj.GetType().InvokeMember(fi.Name
    , BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance
    , null, obj, null);

Ответ 4

См. следующий отрывок из этот документ:

Автоматически реализованные (автоматически реализованные) свойства автоматизируют этот шаблон. Более конкретно, объявлениям без абстрактных свойств разрешено иметь центры доступа с точкой с запятой. Оба аксессора должны присутствовать, и оба должны иметь точки с запятой, но они могут иметь разные модификаторы доступности. Когда свойство указано так, для этого свойства будет автоматически создано резервное поле, и аксессоры будут реализованы для чтения и записи в это фоновое поле. Имя поля поддержки является компилятором, сгенерированным и недоступным для пользователя.

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

Ответ 5

UPDATE: https://github.com/jbevain/mono.reflection поставляется с методом распознавания поля обратной связи, который работает с автоматическими свойствами, сгенерированными С#, VB.NET и F #. Пакет NuGet находится в https://www.nuget.org/packages/Mono.Reflection/

ОРИГИНАЛ: Я закончил этот довольно гибкий метод только для авто-свойств С#. Как видно из других ответов, это не переносимо и не будет работать, если в реализации компилятора используется схема именования поля поддержки, отличная от <PropertyName>k__BackingField. Насколько я видел, все реализации компиляторов С# в настоящее время используют эту схему именования. Компиляторы VB.NET и F # используют другую схему именования, которая не будет работать с этим кодом.

private static FieldInfo GetBackingField(PropertyInfo pi) {
    if (!pi.CanRead || !pi.GetGetMethod(nonPublic:true).IsDefined(typeof(CompilerGeneratedAttribute), inherit:true))
        return null;
    var backingField = pi.DeclaringType.GetField($"<{pi.Name}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
    if (backingField == null)
        return null;
    if (!backingField.IsDefined(typeof(CompilerGeneratedAttribute), inherit:true))
        return null;
    return backingField;
}

Ответ 6

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

Из документации для Автоматически реализованные свойства:

Когда вы объявляете свойство, как показано в следующем примере, компилятор создает личное, анонимное поле поддержки, доступ к которому можно получить только через свойство get и set accessors.

Ответ 7

Автоматически реализованные свойства являются "более ленивой" версией свойства, реализованного вручную, с полем поддержки. Поскольку они не допускают дополнительной логики, единственное, что вы можете с ними сделать, это прочитать или написать "скрытое" частное поле. Вы все равно можете использовать модификатор private для ограничить доступ к одному из аксессуаров (обычно, set будет частным в этом случае), если вы хотите, чтобы ваши частные методы имели эту привилегию.

Если вы хотите получить доступ к частным полям другого класса (например, классу из BCL), вы можете использовать Reflection, чтобы сделать это (< как объяснено в эти примеры), но это был бы неприятный хак, где никто не гарантировал бы, что однобуквенный изменение исходного кода не нарушит ваш код в будущем.

Но поскольку вы уже выбрали автоматическую реализацию, я не вижу причин, по которым вы хотите получить доступ к полю резервного копирования. Доступ к полю напрямую или через автоматически реализованные аксессуры свойств не имеет никакой разницы и никакой выгоды. Вы могли бы, например, утверждать, что вы можете использовать Interlocked, чтобы изменить поле атомарно (что вы не можете сделать для свойства), но это не обеспечивает безотказную "нить", когда все остальные имеют доступ к полю через свойство без атомарности.

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