Можно ли создать ссылочный цикл, используя только типы значений?

В качестве объяснения, возьмите этот тип значения в 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" было бы запрещено.

Другие вопросы по тегам