Могут ли структуры содержать поля ссылочных типов - PullRequest
51 голосов
/ 03 июня 2009

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

Ответы [ 6 ]

79 голосов
/ 03 июня 2009

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

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

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

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

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

18 голосов
/ 03 июня 2009

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

struct Example {
  public readonly string Field1;
}

Только чтение не обязательно, но рекомендуется сделать неизменяемым struct.

4 голосов
/ 11 февраля 2011

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

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

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

4 голосов
/ 03 июня 2009

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

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

2 голосов
/ 03 июня 2009

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

Это зависит.

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

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

1 голос
/ 27 ноября 2017

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

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

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

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

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

Пример, который я нашел, связан ниже.

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

Пример, к которому я пришел, приведен здесь:

https://www.red -gate.com / простой ток / блоги / почему-перечислитель-Структуры-в-а-очень-плохо-идея /

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...