CA2227: Collection properties should be read only

TypeName

CollectionPropertiesShouldBeReadOnly

CheckId

CA2227

Category

Microsoft.Usage

Breaking Change

Breaking

Cause

An externally visible writable property is a type that implements System.Collections.ICollection. Arrays, indexers (properties with the name 'Item'), and permission sets are ignored by the rule.

Rule Description

A writable collection property allows a user to replace the collection with a completely different collection. A read-only property stops the collection from being replaced but still allows the individual members to be set. If replacing the collection is a goal, the preferred design pattern is to include a method to remove all the elements from the collection and a method to re-populate the collection. See the Clear and AddRange methods of the System.Collections.ArrayList class for an example of this pattern.

Both binary and XML serialization support read-only properties that are collections. The System.Xml.Serialization.XmlSerializer class has specific requirements for types that implement ICollection and System.Collections.IEnumerable in order to be serializable.

How to Fix Violations

To fix a violation of this rule, make the property read-only and, if the design requires it, add methods to clear and re-populate the collection.

When to Suppress Warnings

Do not suppress a warning from this rule.

Example

The following example shows a type with a writable collection property and shows how the collection can be replaced directly. Additionally, the preferred manner of replacing a read-only collection property using Clear and AddRange methods is shown.

Imports System
Imports System.Collections

Namespace UsageLibrary

   Public Class WritableCollection

      Dim strings As ArrayList

      Property SomeStrings As ArrayList
         Get 
            Return strings
         End Get 

         ' Violates the rule. 
         Set
            strings = Value
         End Set 
      End Property 

      Sub New()
         strings = New ArrayList( _
            New String() {"IEnumerable", "ICollection", "IList"} )
      End Sub 

   End Class 

   Class ViolatingVersusPreferred

      Shared Sub Main()
         Dim newCollection As New ArrayList( _
            New String() {"a", "new", "collection"} )

         ' strings is directly replaced with newCollection. 
         Dim collection As New WritableCollection()
         collection.SomeStrings = newCollection

         ' newCollection is added to the cleared strings collection.
         collection.SomeStrings.Clear()
         collection.SomeStrings.AddRange(newCollection)
      End Sub 

   End Class 

End Namespace
using System;
using System.Collections;

namespace UsageLibrary
{
   public class WritableCollection
   {
      ArrayList strings;

      public ArrayList SomeStrings
      {
         get { return strings; }

         // Violates the rule. 
         set { strings = value; }
      }

      public WritableCollection()
      {
         strings = new ArrayList(
            new string[] {"IEnumerable", "ICollection", "IList"} );
      }
   }

   class ReplaceWritableCollection
   {
      static void Main()
      {
         ArrayList newCollection = new ArrayList(
            new string[] {"a", "new", "collection"} );

         // strings is directly replaced with newCollection.
         WritableCollection collection = new WritableCollection();
         collection.SomeStrings = newCollection;

         // newCollection is added to the cleared strings collection.
         collection.SomeStrings.Clear();
         collection.SomeStrings.AddRange(newCollection);
      }
   }
}
using namespace System;
using namespace System::Collections;

namespace UsageLibrary
{
   public ref class WritableCollection
   {
   public:
      // Violates the rule. 
      property ArrayList^ SomeStrings;

      WritableCollection()
      {
         SomeStrings = gcnew ArrayList(
            gcnew array<String^> {"IEnumerable", "ICollection", "IList"} );
      }
   };
}

using namespace UsageLibrary;

void main()
{
   ArrayList^ newCollection = gcnew ArrayList(
      gcnew array<String^> {"a", "new", "collection"} );

   // strings is directly replaced with newCollection.
   WritableCollection^ collection = gcnew WritableCollection();
   collection->SomeStrings = newCollection;

   // newCollection is added to the cleared strings collection.
   collection->SomeStrings->Clear();
   collection->SomeStrings->AddRange(newCollection);
}

CA1819: Properties should not return arrays