Collection properties should be read only
A writable collection property lets users replace the whole collection; a read-only one still allows setting individual members. If replacement is needed, prefer clear and re-populate methods (see Clear() and AddRange() on System.Collections.Generic.List<T>).
Binary and XML serialization support read-only collection properties (XmlSerializer has specific requirements for ICollection / IEnumerable types). Matches property setters assigning a collection.
Binary and XML serialization support read-only collection properties (XmlSerializer has specific requirements for ICollection / IEnumerable types). Matches property setters assigning a collection.
How to fix
Make the property read-only, adding clear and re-populate methods if the design requires it.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Collection properties should be read only</Name>
// <Id>ND2300:CollectionPropertiesShouldBeReadOnly</Id>
warnif count > 0
// First find collectionTypes
let collectionInterfaces = ThirdParty.Types.WithFullNameIn(
"System.Collections.ICollection",
"System.Collections.Generic.ICollection<T>")
let collectionTypes = Types.ThatImplementAny(collectionInterfaces)
.Union(collectionInterfaces)
.Union(Types.WithFullName("System.Array"))
.ToHashSetEx()
where collectionTypes.Count() > 0
// Find all Read/Write property that is typed with a collection type
from property in JustMyCode.Properties.Where(
p => p.IsReadWrite &&
p.PropertyType != null &&
collectionTypes.Contains(p.PropertyType))
let propSetter = property.SetMethod
where propSetter != null &&
!propSetter.IsPrivate && // Ignore private setters since this is private implementation detail
!propSetter.ParentType.IsPrivate && // Ignore setters of private types
// Ignore properties of serializable types
!propSetter.ParentType.TypesUsed.Any(t1 => t1.IsAttributeClass &&
(t1.ParentNamespace.Name == "Newtonsoft.Json" || t1.ParentNamespace.Name == "System.Text.Json")
) &&
!propSetter.ParentType.HasAttribute("System.Runtime.Serialization.DataContractAttribute".AllowNoMatch()) &&
!propSetter.ParentType.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) &&
!propSetter.ParentType.HasAttribute("System.SerializableAttribute".AllowNoMatch())
select new {
property,
CollectionType = property.PropertyType,
Debt = 10.ToMinutes().ToDebt(),
Severity = Severity.Medium
}
//<Expl>
// The *{0}* should be read-only because it is typed with the collection *{1}*.
// Doing so favors *encapsulation*, *predictable behavior* and *consistency*.
//</Expl>
//<Desc>
// A writable collection property lets users replace the whole collection; a
// read-only one still allows setting individual members. If replacement is
// needed, prefer clear and re-populate methods (see *Clear()* and
// *AddRange()* on *System.Collections.Generic.List<T>*).
//
// Binary and XML serialization support read-only collection properties
// (*XmlSerializer* has specific requirements for *ICollection* /
// *IEnumerable* types). Matches property setters assigning a collection.
//</Desc>
//<HowToFix>
// Make the property read-only, adding clear and re-populate methods if the
// design requires it.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
.NET Framework Usage / System.Collections: all 4 items
- Collection properties should be read only ND2300
- Don't use .NET 1.x HashTable and ArrayList ND2301
- Caution with List.Contains() ND2302
- Prefer return collection abstraction instead of implementation ND2303