Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

Конечно, и это не плохая практика.

struct Example {
  public readonly string Field1;
}

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

Ответ 3

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

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

Ответ 4

Да, они могут.

Это зависит.

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

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

Ответ 5

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

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

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

Ответ 6

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

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

Я наткнулся на эту ссылку, где программист объявил класс, содержащий поле readonly struct. Поле в его классе - это struct --- it a LinkedList<T>.Enumerator - и оно сломалось, потому что поле readonly --- его собственные методы класса получают копию перечислительной структуры, и состояние копируется и не динамический.

Но, если вы исправите его код, просто удалив readonly из поля struct (который работает); и затем, однако, если вы затем решите сделать свой собственный класс a struct, теперь еще раз, потребитель вашей структуры не сможет использовать его как поле для чтения, иначе они, в свою очередь, будут укушены той же проблемой. (И если это кажется ухищренным, потому что у вас не будет перечислителя, только на самом деле, если он поддерживает Reset!)

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

Ниже приведен пример, который я нашел.

Его класс не является struct, но содержит поле m_Enumerator (и программист предположительно знает, что это struct).

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

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

Но если вы исправите это, оставив поле, объявленное как 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". Его нужно объяснить как пару значений readonly; период; кроме того, что вы что-то знаете, вы можете начать рассуждать о добавлении чего-то!

Пример, который я получил accros, находится здесь:

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