Доступ к свойствам родительского экземпляра? - PullRequest
0 голосов
/ 22 ноября 2018

Допустим, у меня есть несколько классов, которые выглядят примерно так:

Этот класс я назову родительский экземпляр :

public class Foo : Disposable {
    public Foo() {
        Bars = new List<Bar>();
        FullPath = string.empty;
    }
    public Foo(string szInfo) {
        Bars = new List<Bar>();
        ImportantInfo = szInfo;
    }
    ~Foo() => this.Dispose(false);
    /* IDisposible stuff cropped for simplicity */

    public string ImportantInfo {get; internal set;}
    public List<Bar> Bars {get; internal set;}

    public void SomeContainerLoadMethod() {
        /* Add some bars here */
        Bars.Add( new Bar() );
        Bars.Add( new Bar() );
        /* etc... */
    }
}

Как выможно увидеть здесь, родительский экземпляр Foo содержит несколько Bar классов.Я буду называть эти Bar классы в List<Bar> дочерних контейнерах экземпляров в этом вопросе.Вот определение класса Bar в примере кода:

public class Bar : Disposable {
    Bar() { }
    ~Bar() => this.Dispose(false);
    /* IDisposable stuff cropped for simplicity */

    public string CoolBuff {get; internal set;}

    public void SomeCoolStringBufMethod() {
        /* Do something to populate CoolBuff, but I need ImportantInfo! */
    }
}

Как мне получить доступ к ImportantInfo из родительского экземпляра, в дочернем контейнере 's SomeCoolStringBufMethod()?

Вот осложнения этой проблемы:

  1. Выполнение без необходимости создать дублирующее свойство ImportantInfo и передать его ребенкуКонструктор контейнера экземпляра
  2. Выполнение без передачи ImportantInfo в качестве аргумента при вызове метода родительского экземпляра SomeCoolStringBufMethod() из родительского объекта.

Возможно ли, скажем, с помощью System.Reflection, «посмотреть» на тот факт, что Bar является членом Foo и извлечь Foo '* ImportantInfo свойство?

Ответы [ 3 ]

0 голосов
/ 22 ноября 2018

Короткий ответ - НЕТ.

Длинный ответ - теоретически Да, но практически - нет.

Поскольку у вас Bar вообще нет ссылки на Foo, поэтому вы не можетедаже скажите, какие Foo содержат ваши Bar, вы даже не можете сказать, ссылается ли на ваш Bar какой-либо из Foo.

Чтобы выяснить это все, вы должны отследитьназад, кто ссылается на ваш Bar.

В теории это можно сделать с помощью техники, подобной GC, но GC выполняет поиск по ссылкам сверху вниз, что означает от корня GC до Foo, а затем к вашему BarЭто не делает снизу вверх.Вы можете создать свой внешний GC с двойной связью, такой как Foo, Bar Graphic.

На практике это потребует огромных усилий, после этого вы также столкнетесь с проблемой управления собственным циклом GC вашего Foo``Bar рисунка.

Итак, короткий ответ НЕТ.

0 голосов
/ 22 ноября 2018

Номер два - это путь.(И нет, я не пытаюсь быть смешным.)

... передать ImportantInfo в качестве аргумента, когда метод родительского экземпляра SomeCoolStringBufMethod() вызывается из родительского объекта.

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

Есть веские причины, по которым мы обычно не создаем классы с циклическими ссылками между ними.Представьте себе, например, Text.StringBuilder.Что если у него есть ссылка на класс, который его создал, независимо от того, как он получил эту ссылку - через конструктор, отражение или что-то еще.

Что будет StringBuilder делать с этой ссылкой?Чтобы сделать что-либо с этим объектом, кроме вызова ToString(), ему нужно знать тип этого объекта.Но если ему известен тип этого объекта, то это означает, что StringBuilder работает, только если у него есть ссылка на этот тип объекта.Это означает, что класс, который зависит от StringBuilder и StringBuilder, может использоваться только в сочетании друг с другом.

Возвращаясь к вашему классу: что нужно вашему детскому классу?Bar нужен Foo?Это нужно string.Любой класс, который вызывает его метод, может дать ему string.Так зачем соединять это с другим классом?Однажды вам или кому-то еще нужно будет заставить Bar работать без Foo, и тогда у вас будет узел, который можно развязать.

Если Bar зависит от Foo, чтобы получить его ImportantProperty, что также затрудняет юнит-тестирование.Вам нужно будет создать Foo, а затем создать Bar, чтобы Bar мог получить ImportantProperty из Foo.Если это зависит от string, то это легко проверить.Тест должен только создать string.

. В вашем примере передача ImportantProperty конструктору Bar не имеет смысла, потому что это свойство записи Foo.Это означает, что Foo может изменить его, и тогда все Bar будут иметь другое свойство, если вы не создадите все новые.(Возможно, тот факт, что ImportantProperty может измениться, является причиной, по которой вы хотите вернуть ссылку на родителя, но передача string вызову метода все еще решает эту проблему.)

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

0 голосов
/ 22 ноября 2018

Вы не можете.

Два списка, которые вы перечисляете, действительно единственный способ сделать это.

Помните, что любой экземпляр класса существует по адресу в памяти.Переменные просто сообщают вашему приложению, где искать данные в памяти.Так что вы можете использовать отражение, чтобы найти свойство ImportantInfo экземпляра Foo, но какой именно?Где это искать в памяти?Вы должны знать, где в памяти искать.

Вы знаете, где в памяти искать, используя переменную.Так что вам нужно как-то передать переменную в Bar.

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

Небольшое примечание: когда вы передаете string методу, вы не создаете дубликат.Подробнее об этом здесь , если вам интересно.

...