Существует несколько вещей, продолжающихся в этом вопросе...
для структуры возможно реализовать интерфейс, но существуют проблемы, которые появляются с кастингом, переменчивостью и производительностью. Дополнительную информацию см. в этом сообщении: http://blogs.msdn.com/abhinaba/archive/2005/10/05/477238.aspx
В целом, структуры должны использоваться для объектов, которые имеют семантику типа значения. Путем реализации интерфейса на структуре можно столкнуться с упаковкой проблем, поскольку структура брошена и дальше между структурой и интерфейсом. В результате упаковки операции, которые изменяют внутреннее состояние структуры, не могут вести себя правильно.
Структуры реализованы как типы значения, и классы являются ссылочными типами. Если у Вас будет переменная типа Foo, и Вы храните экземпляр Fubar в нем, он "Втиснет его" в ссылочный тип, таким образом побеждая преимущество использования структуры во-первых.
единственная причина я вижу для использования структуры вместо класса, то, потому что это будет тип значения и не ссылочный тип, но структура не может наследоваться классу. Если у Вас есть структура, наследовали интерфейс, и Вы раздаете интерфейсы, Вы теряете ту природу типа значения структуры. Мог бы также просто сделать его классом, если Вам нужны интерфейсы.
(Хорошо добрался ничего серьезного, чтобы добавить, но сделать не, имеют мастерство редактирования все же, таким образом, здесь идет..)
Совершенно Безопасный. Ничто недопустимое с реализацией интерфейсов на структурах. Однако необходимо подвергнуть сомнению, почему Вы хотели бы сделать это.
Однако получение интерфейсной ссылки на структуру УПАКУЕТ это. Так потеря производительности и так далее.
единственный действительный сценарий, о котором я могу думать прямо сейчас, проиллюстрирован в моем сообщении здесь . Когда Вы хотите изменить состояние структуры, сохраненное в наборе, необходимо было бы сделать это через дополнительный интерфейс, представленный на структуре.
Я думаю, что проблема состоит в том, что это вызывает упаковку, потому что структуры являются типами значения, таким образом, существует небольшая потеря производительности.
Эта ссылка предполагает, что могли бы быть другие проблемы с нею...
http://blogs.msdn.com/abhinaba/archive/2005/10/05/477238.aspx
Нет никаких последствий для структуры, реализовывая интерфейс. Например, структуры встроенной системы реализуют интерфейсы как IComparable
и IFormattable
.
Существует очень мало причины типа значения для реализации интерфейса. Так как Вы не можете разделить тип значения на подклассы, можно всегда называть его его конкретным типом.
, Если, конечно, у Вас нет нескольких структур вся реализация того же интерфейса, это могло бы быть незначительно полезно тогда, но в той точке я рекомендую использовать класс и сделать его правильно.
, Конечно, путем реализации интерфейса, Вы упаковываете структуру, таким образом, она теперь находится на "куче", и Вы не будете в состоянии передать ее значением больше... Это действительно укрепляет мое мнение, что необходимо просто использовать класс в этой ситуации.
Структуры точно так же, как классы, которые живут в стеке. Я не вижу оснований, почему они должны быть "небезопасными".
Поскольку никто другой явно не предоставил этот ответ, я добавлю следующее:
Реализация интерфейса в структуре не имеет никаких негативных последствий.
Любая переменная типа интерфейса, используемого для хранения структуры, приведет к использованию упакованного значения этой структуры. Если структура неизменна (хорошо), то в худшем случае это проблема производительности, если только вы:
Оба варианта маловероятны, вместо этого вы, скорее всего, выполните одно из следующих действий:
Возможно, многие разумные причины для структур, реализующих интерфейсы, заключаются в том, что их можно использовать в универсальном контексте с ограничениями . При таком использовании переменная выглядит так:
class Foo<T> : IEquatable<Foo<T>> where T : IEquatable<T>
{
private readonly T a;
public bool Equals(Foo<T> other)
{
return this.a.Equals(other.a);
}
}
new ()
или class
. Тогда это .a НЕ является ссылкой на интерфейс, поэтому он не приводит к тому, что в него помещается блок. Кроме того, когда компилятор C # компилирует универсальные классы и ему необходимо вставить вызовы методов экземпляра, определенных в экземплярах параметра Type T, он может использовать код операции с ограничениями :
Если thisType является типом значения, а thisType реализует метод, тогда ptr передается без изменений как указатель this на инструкцию вызова метода для реализации метода с помощью thisType.
Это позволяет избежать упаковки, и поскольку тип значения реализует интерфейс, должен ] реализует метод, поэтому упаковки не произойдет. В приведенном выше примере вызов Equals ()
выполняется без рамки на this.a 1 .
Большинство структур должны иметь семантику примитивного типа, где побитовые одинаковые значения считаются равными 2 . Среда выполнения предоставит такое поведение в неявном Equals ()
, но это может быть медленным. Также это неявное равенство не представлено как реализация IEquatable
и, таким образом, предотвращает легкое использование структур в качестве ключей для словарей, если они явно не реализуют его сами. Поэтому многие общедоступные типы структур обычно заявляют, что они реализуют IEquatable
(где T
- это они сами), чтобы упростить и повысить производительность, а также согласовать с поведением многих существующих типов значений в CLR BCL.
Все примитивы в BCL реализуют как минимум:
IComparable
IConvertible
IComparable
IEquatable
(И, таким образом, IEquatable
) Многие также реализуют IFormattable
, кроме того, многие типы значений, определенные Системой, такие как DateTime, TimeSpan и Guid, также реализуют многие или все из них. Если вы реализуете аналогичный «широко полезный» тип, такой как структура со сложным числом или некоторые текстовые значения фиксированной ширины, то реализация многих из этих общих интерфейсов (правильно) сделает вашу структуру более полезной и удобной.
Очевидно, что если интерфейс строго подразумевает изменчивость (например, ICollection
), то реализация его - плохая идея, так как это будет означать, что вы либо сделали структуру изменяемой ( приводящие к уже описанным видам ошибок, когда модификации происходят в упакованном значении, а не в исходном), или вы сбиваете с толку пользователей, игнорируя последствия таких методов, как Add ()
или выбрасывая исключения.
Многие интерфейсы НЕ предполагают изменчивости (например, IFormattable
) и служат идиоматическим способом согласованного предоставления определенных функций. Часто пользователь структуры не заботится о накладных расходах на упаковку для такого поведения.
При разумном подходе к неизменяемым типам значений реализация полезных интерфейсов является хорошей идеей
1 : Обратите внимание, что компилятор может использовать это при вызове виртуальных методов для переменных, которые известны как имеющие определенный тип структуры, но в которых требуется вызывать виртуальный метод. Например:
List<int> l = new List<int>();
foreach(var x in l)
;//no-op
Перечислитель, возвращаемый List, является структурой, оптимизацией, позволяющей избежать выделения при перечислении списка (с некоторыми интересными последствиями ). Однако семантика foreach указывает, что если перечислитель реализует IDisposable
, то Dispose ()
будет вызываться после завершения итерации. Очевидно, что если это произойдет через упакованный вызов, это исключит любую выгоду от того, что перечислитель является структурой (на самом деле это было бы хуже). Хуже, если вызов dispose каким-либо образом изменяет состояние перечислителя, это может произойти с экземпляром в штучной упаковке, и в сложных случаях может появиться множество тонких ошибок. Следовательно, IL, генерируемый в такой ситуации:
IL_0001: newobj System.Collections.Generic.List..ctor IL_0006: stloc.0 IL_0007: nop IL_0008: ldloc.0 IL_0009: callvirt System.Collections.Generic.List.GetEnumerator IL_000E: stloc.2 IL_000F: br.s IL_0019 IL_0011: ldloca.s 02 IL_0013: call System.Collections.Generic.List.get_Current IL_0018: stloc.1 IL_0019: ldloca.s 02 IL_001B: call System.Collections.Generic.List.MoveNext IL_0020: stloc.3 IL_0021: ldloc.3 IL_0022: brtrue.s IL_0011 IL_0024: leave.s IL_0035 IL_0026: ldloca.s 02 IL_0028: constrained. System.Collections.Generic.List.Enumerator IL_002E: callvirt System.IDisposable.Dispose IL_0033: nop IL_0034: endfinally
Таким образом, реализация IDisposable не вызывает каких-либо проблем с производительностью, и (прискорбный) изменяемый аспект перечислителя сохраняется, если метод Dispose действительно что-то сделает!
2: double и float - исключения из этого правила, где значения NaN не считаются равными.