Так как это получило отрицательное голосование, я пытаюсь немного переписать, чтобы увидеть, может ли это стать более ясным. Этот вопрос старый; но хорошо! Недавно я также натолкнулся на несколько ссылок, которые развивают это.
Суть, которую я пытаюсь добавить, заключается в том, что если вы делаете объявляете ссылочные поля, вы должны иметь возможность обосновывать вашего собственного блока: когда кто-то использует вашу структуру. Особое замечание, которое я добавил, было на самом деле только объявлением поля структуры только для чтения; но в этом случае поля в вашей структуре могут изменить свои результаты; и об этом трудно рассуждать.
Я наткнулся на эту ссылку, где программист объявил класс , содержащий поле 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 / простой ток / блоги / почему-перечислитель-Структуры-в-а-очень-плохо-идея /