Можно ли создать ссылочный цикл, используя только типы значений? - PullRequest
5 голосов
/ 04 сентября 2010

В качестве пояснения, возьмите этот тип значения в 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 эффективно предотвращает такой контрольный цикл?

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


EDIT

Как предположил Брайан , действительно можно изменить упакованное значение, не распаковывая его, приведя его к типу интерфейса вместо типа значения. Итак, учитывая этот код:

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 ]

2 голосов
/ 04 сентября 2010

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

Быстрый образец

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
    }
}

Я разделяю вопрос Брайана о том, что это даст вам преимущество.

1 голос
/ 04 сентября 2010

Честно говоря, я не пробовал, но посмотрим, есть ли свойство Value на интерфейсе, и затем использование интерфейса в качестве вашей коробки позволяет вам изменять саму коробку вместо новой копии.

Я смутно чувствую, что это возможно, хотя я не уверен, почему я так думаю. Полезно, а?

0 голосов
/ 04 сентября 2010

Я не знал, что структуры могут реализовывать интерфейсы.Это кажется действительно странным;для чего это хорошо?Нелюбовь к структурам вообще или к структурам со свойствами и методами, которые воздействуют на них?Это очень плохо. Net не позволяет объявлять определенные свойства и методы структуры как мутаторы, использование которых в структурах «ReadOnly» было бы запрещено.

...