Можно ли создать ссылочный цикл, используя только типы значений?
В качестве объяснения, возьмите этот тип значения в C#:
struct ObjRef
{
public object Value;
public ObjRef(object value) { Value = value; }
}
Я могу представить граф объектов, в котором есть два экземпляра этого типа в штучной упаковке, каждый из которых содержит ссылку на другой. Это то, что я имею в виду под ссылочным циклом только с типами значений.
Мой вопрос заключается в том, может ли такой граф объектов когда-либо быть построен в.NET. Концептуально конструкция, если она существует, будет выглядеть следующим образом:
object left = new ObjRef();
object right = new ObjRef(left);
left.Value = right;
но, очевидно, последняя строка там не является допустимым C#. Делаем последнюю строчку:
((ObjRef)left).Value = right;
не достигает результата, как при касте распаковывает left
и вы в конечном итоге мутировать копию. Так что, по крайней мере, в прямом C# это не выглядит возможным.
Кто-нибудь знает, может ли конструкция быть достигнута с помощью отражения, небезопасного кода, dynamic
Код IL или любым другим способом? Или кто-нибудь может показать, что CLR эффективно предотвращает такой ссылочный цикл?
Обратите внимание, что я на самом деле не хочу создавать такой граф объектов. Скорее ответ может повлиять на разработку алгоритмов, которые работают с графами объектов, таких как форматеры сериализации / десериализации.
РЕДАКТИРОВАТЬ
Как предположил Брайан, действительно можно изменить упакованное значение, не распаковывая его, приведя его к типу интерфейса вместо типа значения. Итак, учитывая этот код:
interface IObjRef
{
IObjRef Value { get; set; }
}
struct ObjRef : IObjRef
{
IObjRef value;
public IObjRef Value { get { return value; } set { this.value = value; } }
public ObjRef(IObjRef value) { this.value = value; }
}
тогда опорный цикл, который я описываю, может быть построен следующим образом:
IObjRef left = new ObjRef();
IObjRef right = new ObjRef(left);
left.Value = right;
Что в основном оставляет нам причину № 72, почему изменчивые типы значений являются злом.
3 ответа
Это возможно, используя интерфейс и имея тип значения, реализующий интерфейс и ссылающийся друг на друга. Это позволяет им создавать циклический переход между значениями в штучной упаковке, так как структура при использовании с интерфейсной ссылкой будет упакована.
Быстрый образец
interface ICycle
{
void SetOther(ICycle other);
}
struct Cycle : ICycle
{
ICycle value;
public void SetOther(ICycle other)
{
value = other;
}
}
class Example
{
static void CreateCycle()
{
ICycle left = new Cycle(); // Left is now boxed
ICycle right = new Cycle(); // Right is now boxed
left.SetOther(right);
right.SetOther(left); // Cycle
}
}
Хотя я разделяю вопрос Брайана о том, что это даст вам преимущество.
Честно говоря, я не пробовал, но посмотрите, если есть Value
свойство находится на интерфейсе, а затем использование интерфейса в качестве поля позволяет вам изменять саму коробку вместо новой копии.
Я смутно чувствую, что это возможно, хотя я не уверен, почему я так думаю. Полезно, а?
Я не знал, что структуры могут реализовать интерфейсы. Это кажется действительно странным; для чего это хорошо? Не нравится ли структурам вообще или структурам со свойствами и методами, которые воздействуют на них? Это очень плохо.NET не позволяет объявлять определенные свойства и методы структуры как мутаторы, чье использование в структурах "ReadOnly" было бы запрещено.