Share via


Field Design

Fields hold data associated with an object. In the vast majority of scenarios any non-static fields in a library should not be visible to developers. The following guidelines help you correctly use fields in your library design.

Do not provide instance fields that are public or protected.

Public and protected fields do not version well and are not protected by code access security demands. Instead of using publicly visible fields, use private fields and expose them through properties.

Do use constant fields for constants that will never change.

For example, the Math class defines E and PI as static constants.

The compiler inserts the values of const fields directly into the calling code, which means that const values can never be changed without the risk of introducing a compatibility issue.

Do use public static read-only fields for predefined object instances.

For example, the DateTime class provides static read-only fields that you can use to obtain DateTime objects set to the maximum or minimal time value. See MaxValue and MinValue.

Do not assign instances of mutable types to read-only fields.

The objects created using a mutable type can be modified after they are created. For example, arrays and most collections are mutable types while Int32, Uri, and String are immutable types. For fields that hold a mutable reference type, the read-only modifier prevents the field value from being overwritten but does not protect the mutable type from modification.

The following code example demonstrates the problem with using read-only fields. The BadDesign class creates a read-only field and exposes it using a read-only property. This does not prevent the ShowBadDesign class from modifying the contents of the read-only field.

Imports System

Namespace Examples.DesignGuidelines.Fields

    Public Class BadDesign

        Public Readonly dataValues as Integer() = {1,2,3}

        Public ReadOnly Property Data as Integer ()

            Get 
                Return dataValues
            End Get
        End Property

        Public Sub WriteData()

            For Each i as Integer In dataValues

                Console.Write ("{0} ", i)
            Next i
            Console.WriteLine()
        End Sub
    End Class

    Public Class ShowBadDesign

        Public Shared Sub Main()

            Dim bad as BadDesign = new BadDesign()
            ' The following line will write: 1 2 3
            bad.WriteData()

            Dim badData as Integer() = bad.Data
            For i as Integer  = 0 To badData.Length -1

                badData(i) = 0
            Next i

            ' The following line will write: 0 0 0
            ' because bad's data has been modified.
            bad.WriteData()
        End Sub
    End Class
End Namespace
using System;

namespace Examples.DesignGuidelines.Fields
{
    public class BadDesign
    {
        public readonly int[] data = {1,2,3};

        public int [] Data
        {
            get {return data;}
        }
        public void WriteData()
        {
            foreach (int i in data)
            {
                Console.Write ("{0} ", i);
            }
            Console.WriteLine();
        }
    }
    public class ShowBadDesign
    {
        public static void Main()
        {
            BadDesign bad = new BadDesign();
            // The following line will write: 1 2 3
            bad.WriteData();

            int[] badData = bad.Data;
            for (int i = 0; i< badData.Length; i++)
            {
                badData[i] = 0;
            }
            // The following line will write: 0 0 0
            // because bad's data has been modified.
            bad.WriteData();
        }
    }
}
using namespace System;

namespace Examples { namespace DesignGuidelines { namespace Fields
{
    public ref class BadDesign
    {
    public:
        static const array<int>^ data = {1,2,3};

        property array<int>^ Data
        {
            array<int>^ get() {return data;}
        }

        void WriteData()
        {
            for each (int i in data)
            {
                Console::Write ("{0} ", i);
            }
            Console::WriteLine();
        }
    };

    public ref class ShowBadDesign
    {
    public:
        static void Main()
        {
            BadDesign^ bad = gcnew BadDesign();
            // The following line will write: 1 2 3
            bad->WriteData();

            array<int>^ badData = bad->Data;
            for (int i = 0; i< badData->Length; i++)
            {
                badData[i] = 0;
            }
            // The following line will write: 0 0 0
            // because bad's data has been modified.
            bad->WriteData();
        }
    };
}}}

Portions Copyright 2005 Microsoft Corporation. All rights reserved.

Portions Copyright Addison-Wesley Corporation. All rights reserved.

For more information on design guidelines, see the "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" book by Krzysztof Cwalina and Brad Abrams, published by Addison-Wesley, 2005.

See Also

Other Resources

Member Design Guidelines

Design Guidelines for Developing Class Libraries