Разработка структуры

Обновлен: Ноябрь 2007

Структуры имеют тип значения. Они размещаются на стеке или в области данных и становятся недоступны, когда выходят из области видимости. В общем случае тип значения эффективен для размещения и освобождения, однако если он используется в сценарии, который требует значительного количества преобразований упаковки-распаковки, этот тип работает хуже по сравнению со ссылочным типом. Дополнительные сведения см. в разделе Упаковка-преобразование и распаковка-преобразование (Руководство по программированию на C#).

Дополнительные сведения о типах значения и ссылочных типах см. в разделе Общие сведения о системе общих типов (CTS).

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

Если в структуре определен конструктор по умолчанию, то при создании массивов структуры среда CLR автоматически вызовет конструктор по умолчанию для каждого элемента массива.

Некоторые компиляторы, такие как компилятор C#, не позволяют структурам иметь конструктор по умолчанию.

Не реализуйте System.IEquatable`1 на типах значения.

IEquatable<T> предпочтительнее, чем Equals для сравнения двух типов значения. При использовании интерфейса предотвращается негативное влияние преобразования упаковки и управляемого отражения.

Обязательно убедитесь, что состояние, в котором все данные экземпляра имеют значения false, null иле нулевые (в зависимости от конкретного случая) является приемлемым.

Если вы будете следовать этому указанию, вновь созданные вами экземпляры типов значения не останутся в неиспользуемом состоянии. Например, следующая структура спроектирована неправильно. Параметризованный конструктор предназначен для обеспечения корректного состояния, но конструктор не выполняется, когда создается массив структур. Это означает, что экземпляр поля label инициализируется в null (Nothing в Visual Basic), что недопустимо для реализации этой структуры для метода ToString.

Public Structure BadStructure

    Private label As String

    Private width As Integer

    Private length As Integer

    Public Sub New(ByVal labelValue As String, ByVal widthValue As Integer, ByVal lengthValue As Integer)
        If ((labelValue = Nothing) _
                    OrElse (labelValue.Length = 0)) Then
            Throw New ArgumentNullException("label")
        End If
        label = labelValue
        width = widthValue
        length = lengthValue
    End Sub

    Public Overrides Function ToString() As String
        ' Accessing label.Length throws a NullReferenceException
        ' when label is null.
        Return String.Format("Label length: {0} Label: {1} Width: {2} Length: {3}", label.Length, label, width, length)
    End Function
End Structure
public  struct BadStructure 
{
    string label;
    int width;
    int length;

    public BadStructure (string labelValue, int widthValue, int lengthValue) 
    {
        if (labelValue == null || labelValue.Length ==0) 
        {
            throw new ArgumentNullException("label");
        }
        label = labelValue;
        width = widthValue;
        length = lengthValue;
    }

    public override string ToString() 
    {
        // Accessing label.Length throws a NullReferenceException
        // when label is null.
        return String.Format("Label length: {0} Label: {1} Width: {2} Length: {3}",
            label.Length, label, width,length);
    }
}

В следующем примере программного кода при описании структуры GoodStructure не делается никаких предположений о состоянии поля label. Метод ToString разработан для управления меткой null.

Public Structure GoodStructure

    Private label As String

    Private width As Integer

    Private length As Integer

    Public Sub New(ByVal labelValue As String, ByVal widthValue As Integer, ByVal lengthValue As Integer)
        label = labelValue
        width = widthValue
        length = lengthValue
    End Sub

    Public Overrides Function ToString() As String
        ' Handle the case where label might be 
        ' initialized to null;
        Dim formattedLabel As String = label
        Dim formattedLableLength As Integer
        If (formattedLabel = Nothing) Then
            formattedLabel = "<no label value specified>"
            formattedLableLength = 0
        Else
            formattedLableLength = label.Length
        End If
        Return String.Format("Label Length: {0} Label: {1} Width: {2} Length: {3}", formattedLableLength, formattedLabel, width, length)
    End Function
End Structure

public  struct GoodStructure 
{
    string label;
    int width;
    int length;

    public GoodStructure (string labelValue, int widthValue, int lengthValue) 
    {
        label = labelValue;
        width = widthValue;
        length = lengthValue;
    }

    public override string ToString() 
    {
        // Handle the case where label might be 
        // initialized to null;
        string formattedLabel = label;
        int formattedLableLength;
        if (formattedLabel == null)
        {
            formattedLabel = "<no label value specified>";
            formattedLableLength = 0;
        } else
        {
            formattedLableLength = label.Length;
        }
        return String.Format("Label Length: {0} Label: {1} Width: {2} Length: {3}",
            formattedLableLength, formattedLabel, width, length);
    }
}

Не расширяйте явно System.ValueType.

Некоторые компиляторы не позволяют расширять ValueType.

Охраняется авторским правом Copyright 2005 Microsoft Corporation. Все права защищены.

Охраняется авторским правом Copyright Addison-Wesley Corporation. Все права защищены.

Дополнительные сведения о руководствах по разработке см. в книге "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries", Krzysztof Cwalina and Brad Abrams, Addison-Wesley, 2005.

См. также

Основные понятия

Выбор между классами и структурами

Другие ресурсы

Правила разработки типов

Руководство по разработке библиотек классов