Как решить круговую ссылку?

Как вы решаете круглые эталонные проблемы, такие как класс A, имеет класс B как один из его свойств, тогда как класс B имеет класс A как один из его свойств?

Как сделать архитектор для таких проблем?

Если вы примете пример NHibernate, между объектами будет отношение родитель-потомок.

Как он может обрабатывать эти родительские дочерние сценарии?

Ответ 1

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

перед

public class Foo
{
    Bar myBar;
}

public class Bar
{
    Foo myFoo;
}

График зависимости:

Foo     Bar
 ^       ^
 |       |
Bar     Foo

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

после

public interface IBar
{
}

public class Foo
{
    IBar myBar;
}

public class Bar : IBar
{
    Foo myFoo;
}

График зависимости:

Foo, IBar     IBar
    ^          ^
    |          |
   Bar        Foo

Оба Foo и Bar зависят от IBar. Нет циклической зависимости, и если IBar помещается в собственную сборку, Foo и Bar, находящиеся в отдельных сборках, больше не будут проблемой.

Ответ 2

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

Ответ 3

В отличие от С++ (например), С# не нуждается в форвардных объявлениях для разрешения круговых ссылок. Следовательно:

public class A
{
    public B B { get;set; }
}

public class B
{
    public A A { get;set; }
}

Однако это часто является показателем сомнительных дизайнерских решений.

Ответ 4

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

  • Переместите общий ссылочный код в проект утилиты в вашем решении, а другие проекты ссылаются на проект Utility
  • Используйте интерфейс, как объяснил "Ed Bayiates" в своем ответе.
  • Если это небольшое количество простого/общего кода, то перепишите его для одного из классов, поэтому вам не нужно ссылаться на него в круговой зависимости. (мой наименее любимый)

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

Щелкните правой кнопкой мыши ссылки на проект и выберите "Добавить ссылку...". Затем в появившемся диалоговом окне перейдите на вкладку "Обзор" и кнопку "Обзор". Оттуда вы можете найти DLL и выбрать его. Это в лучшем случае работа и может вызвать проблемы с сборкой, особенно если обе библиотеки DLL обновляются часто и/или имеют множество зависимостей. Я не рекомендую этот метод, но он работает в крайнем случае.

FiSSH

Ответ 5

- хорошая идея, однако, если вы ищете более быстрое решение, чем переделываете архитектуру столь многих вещей, попробуйте создать одну библиотеку классов dll, которая будет содержать все ваши структуры данных, ваш основной проект содержит ваш пользовательский интерфейс, который нуждается в этих данных, а затем любые другие dll, которые вы хотите добавить, могут также обращаться к этой DLL-структуре данных, поэтому у них есть вся информация, которую они должны запускать, но все же может быть раздельной - это называется шаблоном проектирования tri force -

Ответ 6

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

Чтобы решить проблему циклических ссылок в С#, вы должны использовать сборщик мусора. Он обнаруживает и собирает циркулярные ссылки. Сборщик мусора начинается с локального и статического и помечает каждый объект, который может быть достигнут через их потомков.

Благодаря этому вы можете справиться с проблемами с помощью циклических ссылок.

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

public class A
        {
            B Two;
        }
public class B
        {
            A one;
        }

Чтобы решить проблему, создайте интерфейс -

public interface myInterface {
}

public class A {
   myInterface Two;
}

public class B: myInterface {
   A one;
}