Структуры могут содержать поля ссылочных типов

структуры могут содержать поля ссылочных типов? А если могут, то это плохая практика?

6 ответов


Да, они могут. Это хорошая идея? Ну, это зависит от ситуации. Лично я редко создаю свои собственные структуры... Я бы относился к любой новой пользовательской структуре с определенной долей скептицизма. Я не предполагаю, что это всегда неправильный вариант, просто ему нужен более четкий аргумент, чем класс.

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

MyValueType foo = ...;
MyValueType bar = foo; // Value type, hence copy...

foo.List.Add("x");
// Eek, bar's list has now changed too!

изменяемые структуры-это зло. Неизменяемые структуры со ссылками на изменяемые типы являются подлым злом по-разному.


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

struct Example {
  public readonly string Field1;
}

readonly не обязательно, но это хорошая практика, чтобы сделать struct неизменяемым.


Да, это возможно, и да, это обычно плохая практика.

Если вы посмотрите на саму .NET framework, вы увидите, что практически все структуры содержат только примитивные типы значений.


Да они могут.

Это зависит.

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

но это зависит от ситуации.


причина, по которой вы не можете иметь изменяемые структуры, заключается в поведении ссылочных типов. Прочтите эту статью:http://www.yoda.arachsys.com/csharp/parameters.html

когда у вас есть структура, содержащая объект (все, что не является примитивом, таким как int или double), и вы копируете экземпляр структуры, объект внутри не копируется "глубоко", потому что это просто ссылка (указатель) на место памяти, содержащее фактический класс. Так что если вы копируете изменяемая структура, содержащая экземпляры класса, копия будет ссылаться на те же экземпляры, что и оригинал (следовательно, список bar изменяется выше).

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


так как это получил downvote, я пытаюсь переписать немного, чтобы увидеть, если это может стать яснее. Этот вопрос стар, но хорош! Недавно я также наткнулся на несколько ссылок, которые подробно останавливаются на этом.

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

я наткнулся на эту ссылку, где программист объявлен класс содержащий readonly и исправьте это, объявив поле как interface тип --- IEnumerator<int>.

но, если do исправьте это, оставив поле объявленным как struct и объявить его не readonly, и затем выберите, чтобы определить свой класс как struct, потом теперь если кто-то объявляет экземпляр код struct как readonly поле в некотором классе,снова они теряют!

Е. Г.:

public class Program
{
    private struct EnumeratorWrapper : IEnumerator<int>
    {
        // Fails always --- the local methods read the readonly struct and get a copy
        //private readonly LinkedList<int>.Enumerator m_Enumerator;

        // Fixes one: --- locally, methods no longer get a copy;
        // BUT if a consumer of THIS struct makes a readonly field, then again they will
        // always get a copy of THIS, AND this contains a copy of this struct field!
        private LinkedList<int>.Enumerator m_Enumerator;

        // Fixes both!!
        // Because this is not a value type, even a consumer of THIS struct making a
        // readonly copy, always reads the memory pointer and not a value
        //private IEnumerator<int> m_Enumerator;


        public EnumeratorWrapper(LinkedList<int> linkedList) 
            => m_Enumerator = linkedList.GetEnumerator();


        public int Current
            => m_Enumerator.Current;

        object System.Collections.IEnumerator.Current
            => Current;

        public bool MoveNext()
            => m_Enumerator.MoveNext();

        public void Reset()
            => ((System.Collections.IEnumerator) m_Enumerator).Reset();

        public void Dispose()
            => m_Enumerator.Dispose();
    }


    private readonly LinkedList<int> l = new LinkedList<int>();
    private readonly EnumeratorWrapper e;


    public Program()
    {
        for (int i = 0; i < 10; ++i) {
            l.AddLast(i);
        }
        e = new EnumeratorWrapper(l);
    }


    public static void Main()
    {
        Program p = new Program();

        // This works --- a local copy every time
        EnumeratorWrapper e = new EnumeratorWrapper(p.l);
        while (e.MoveNext()) {
            Console.WriteLine(e.Current);
        }

        // This fails if the struct cannot support living in a readonly field
        while (p.e.MoveNext()) {
            Console.WriteLine(p.e.Current);
        }
        Console.ReadKey();
    }
}

если вы объявите struct С interface поле, вы не будете знать, что у вас там, но все же вы можете рассуждать больше о том, что вы получаете, когда просто ссылаетесь на это! Это очень интересно; но только потому, что язык дает очень много свобод с struct: вам нужно начать с чего-то очень простого; и добавить только то, о чем вы можете конкретно рассуждать!

еще один момент заключается в том, что ссылка также говорит, что вы должны определите значения по умолчанию как разумные; это не возможно со ссылочным полем! Если вы случайно вызываете конструктор по умолчанию - - - всегда возможно с struct - - - тогда вы получаете нулевую ссылку.

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

... Слишком много людей начинают объяснять структуры как "просто класс, но"... X, y, z, 1, 2, alpha, beta disco". Это должно быть объяснено как пара значений только для чтения; точка; кроме того, теперь, когда вы что-то знаете, вы можете начать рассуждать о добавлении чего-то!

пример я пришел accros здесь:

https://www.red-gate.com/simple-talk/blogs/why-enumerator-structs-are-a-really-bad-idea/