Domanda

While running Code Analysis on an existing project I came across the messages Do not expose generic lists and Collection properties should be read only. However, this class is used to read/write from/to an xml configuration file. Is it possible to make this class comply to CA1002 and CA2227 or do I have to suppress these rules for XML-related classes (there are a lot of them in the project)?

EDIT

Changing List<string> to Collection<string> solved CA1002. Still no clue on how to solve CA2227 and still be able to (de)serialize the whole thing.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Xml.Serialization;

/// <summary>
/// Class containing the Configuration Storage
/// </summary>
[XmlRoot("Configuration")]
public class ConfigurationStorage
{
    /// <summary>
    /// Gets or sets the list of executers.
    /// </summary>
    [XmlArray("Executers")]
    [XmlArrayItem("Executer")]
    public Collection<string> Executers { get; set; }

    /// <summary>
    /// Gets or sets the list of IPG prefixes.
    /// </summary>
    [XmlArray("IpgPrefixes")]
    [XmlArrayItem("IpgPrefix")]
    public Collection<string> IpgPrefixes { get; set; }

}

Reading the xml-file:

    public static ConfigurationStorage LoadConfiguration()
    {
        if (File.Exists(ConfigFile))
        {
            try
            {
                using (TextReader r = new StreamReader(ConfigFile))
                {
                    var s = new XmlSerializer(typeof(ConfigurationStorage));
                    var config = (ConfigurationStorage)s.Deserialize(r);
                    return config;
                }
            }
            catch (InvalidOperationException invalidOperationException)
            {
                throw new StorageException(
                    "An error occurred while deserializing the configuration XML file.", invalidOperationException);
            }
        }
    }
È stato utile?

Soluzione

How about:

/// <summary>
/// Class containing the Configuration Storage
/// </summary>
[XmlRoot("Configuration")]
public class ConfigurationStorage {
  /// <summary>
  /// Gets or sets the list of executers.
  /// </summary>
  [XmlArray("Executers")]
  [XmlArrayItem("Executer")]
  public Collection<string> Executers { get; private set; }

  /// <summary>
  /// Gets or sets the list of IPG prefixes.
  /// </summary>
  [XmlArray("IpgPrefixes")]
  [XmlArrayItem("IpgPrefix")]
  public Collection<string> IpgPrefixes { get; private set; }

  public ConfigurationStorage() {
    Executers = new Collection<string>();
    IpgPrefixes = new Collection<string>();
  }
}

This will still work for xml serialization/deserialization.

Altri suggerimenti

If you read the documentation on MSDN, you see a note:

The XmlSerializer gives special treatment to classes that implement IEnumerable or ICollection. A class that implements IEnumerable must implement a public Add method that takes a single parameter. The Add method's parameter must be of the same type as is returned from the Current property on the value returned from GetEnumerator, or one of that type's bases. A class that implements ICollection (such as CollectionBase) in addition to IEnumerable must have a public Item indexed property (indexer in C#) that takes an integer, and it must have a public Count property of type integer. The parameter to the Add method must be the same type as is returned from the Item property, or one of that type's bases. For classes that implement ICollection, values to be serialized are retrieved from the indexed Item property, not by calling GetEnumerator.

So, I think, if you fall in line with this special treatment, you'll have better code that works with XmlSerializer, doesn't use a legacy namespace, and satisfies Code Analysis warnings in the right way, rather than exploiting an oversight in the rule.

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

/// <summary>
/// Class containing the Configuration Storage
/// </summary>
[XmlRoot("Configuration")]
public class ConfigurationStorage
{
    // The executers.
    private readonly ICollection<string> executers = new List<string>();

    // The IPG prefixes.
    private readonly ICollection<string> ipgPrefixes = new List<string>();

    /// <summary>
    /// Gets the list of executers.
    /// </summary>
    [XmlArray("Executers")]
    [XmlArrayItem("Executer")]
    public ICollection<string> Executers
    { 
        get
        {
            return this.executers;
        }
    }

    /// <summary>
    /// Gets the list of IPG prefixes.
    /// </summary>
    [XmlArray("IpgPrefixes")]
    [XmlArrayItem("IpgPrefix")]
    public ICollection<string> IpgPrefixes
    { 
        get
        {
            return this.ipgPrefixes;
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top