0 out of 3 rated this helpful - Rate this topic

CA2227: Collection properties should be read only

TypeName

CollectionPropertiesShouldBeReadOnly

CheckId

CA2227

Category

Microsoft.Usage

Breaking Change

Breaking

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.

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.

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.

Do not suppress a warning from this rule.

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.


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);
      }
   }
}


Did you find this helpful?
(1500 characters remaining)
Community Content Add
Annotations FAQ
XmlSerializer generates an exception if it is marked as "private set" (workaround)

After having the same issue as DominicZ, I found a blog posting at http://blog.andreloker.de/post/2009/08/07/XmlSerializer-and-automatic-collection-properties.aspx that explained that you cannot use auto-backed property and fix this issue.  The property must have a backing filed and the set or private set must be removed completely (only a get, and the property is constructed using the backing field directly).

This worked for me. I was able to serialize the class after doing this.

Arrays
The documentation talks about a special case for Array but FxCop (10 at least) report setters and Array property as errors.
Disagreeing with Code Analysis Rules
The MSDN Library community content is designed primarily to enable MSDN users to provide feedback, extensions and explanations of the documentation. It is not a good way to have issues with the code analysis rules themselves addressed. To discuss the implementation of code analysis rules, post your questions or opinions on the Visual Studio Code Analysis and Code Metrics forum (http://social.msdn.microsoft.com/Forums/en-US/vstscode/). To provide feedback directly to the development team, you can file a bug on the Connect site for Visual Studio 2010 and .NET Framework 4 (https://connect.microsoft.com/VisualStudio). The dev team encourages your feedback through these channels.
XmlSerializer generates an exception if it is marked as "private set"

I have found that if you do use a private set on the collection, the XmlSerializer with throw an exception when constructed. 

System.InvalidOperationException
Unable to generate a temporary class (result=1).
error CS0200: Property or indexer 'ConsoleApplication1.Strings.Collection' cannot be assigned to -- it is read only

The following code demonstrates this:

public class MyStringCollection : Collection<string> {}
public class Strings
{    
 public Strings()    
 {        
  this.Collection = new MyStringCollection();    
 }     
 
 public MyStringCollection Collection { get; private set; }
}
class Program
{    
 static void Main(string[] args)    
 {        
  Strings s = new Strings();
  s.Collection.Add("string1");
  s.Collection.Add("string2");        
  s.Collection.Add("string3");
  //Throws an error
  XmlSerializer ser = new XmlSerializer(typeof(Strings));
  StringBuilder sb = new StringBuilder();
  StringWriter sw = new StringWriter(sb); 
  ser.Serialize(sw, s);        
  string text = sb.ToString();    
 }
}