【问题标题】:Serializing a collection and comply to Code Analysis序列化集合并遵守代码分析
【发布时间】:2013-08-08 05:55:25
【问题描述】:

在现有项目上运行代码分析时,我遇到了消息不要公开通用列表集合属性应该是只读的。 但是,该类用于读取/写入 xml 配置文件。 是否有可能使这个类符合CA1002CA2227 或者我必须为 XML 相关类禁止这些规则(项目中有很多)?

编辑

List<string> 更改为 Collection<string> 解决了 CA1002。 仍然不知道如何解决 CA2227 并且仍然能够(反)序列化整个事情。

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

}

读取 xml 文件:

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

【问题讨论】:

  • 暴露Collection&lt;T&gt; 而不是List&lt;T&gt; 侧步警告但会使您的代码更臭。警告是告诉你公开IList&lt;T&gt;ICollection&lt;T&gt; 或者IEnumerable&lt;T&gt; 而不是List&lt;T&gt;。但是,正如您所注意到的,标准XMLSerializer 不是很灵活。我更喜欢没有using System.Collections.ObjectModel; 的代码,Collection 是您在该命名空间中使用的唯一类型。
  • @Jodrell 我同意,但我的手在这里有点束缚。现在至少我的老板很高兴,因为警告消失了;-)

标签: c# xml-serialization .net-4.5 code-analysis


【解决方案1】:

怎么样:

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

这仍然适用于 xml 序列化/反序列化。

【讨论】:

  • 我认为它不会反序列化。 XmlSerializer如何设置私有属性?
  • 这确实有效。我的印象是 getter 和 setter 都必须是公开的。请参阅 MSDN:XML 序列化是将对象的公共属性和字段转换为序列格式(在本例中为 XML)以进行存储或传输的过程。反序列化从 XML 输出重新创建原始状态的对象。
  • @Jodrell 我刚刚在这里测试过,我能够读取 xml 并向其中添加一个元素。
  • 就在规则的底部 - msdn.microsoft.com/en-us/library/ms182327.aspx 二进制和 XML 序列化都支持作为集合的只读属性。 System.Xml.Serialization.XmlSerializer 类对实现 ICollection 和 System.Collections.IEnumerable 以便可序列化的类型有特定要求。
  • @Koen 我猜它可以工作,因为Collection 实现了ICollection
【解决方案2】:

如果您阅读documentation on MSDN,您会看到一条注释:

XmlSerializer 对实现的类给予特殊处理 IEnumerable 或 ICollection。实现 IEnumerable 的类必须 实现一个采用单个参数的公共 Add 方法。添加 方法的参数必须与从 从 GetEnumerator 返回的值的当前属性,或其中之一 该类型的基础。实现 ICollection 的类(如 CollectionBase) 除了 IEnumerable 还必须有一个 public Item 采用整数的索引属性(C# 中的索引器),它必须 具有整数类型的公共 Count 属性。添加的参数 方法必须与从 Item 属性返回的类型相同,或者 该类型的基地之一。对于实现 ICollection 的类, 要序列化的值是从索引的 Item 属性中检索的, 不是通过调用 GetEnumerator。

所以,我认为,如果您符合这种特殊处理,您将拥有更好的代码,可以与 XmlSerializer 一起使用,不使用旧命名空间,并以正确的方式满足代码分析警告,而不是而不是利用规则中的疏忽。

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

【讨论】:

  • 刚试了你的建议,但是没有读取配置。没有抛出异常,但执行者列表保持为空。当我将类型改回Collection时,反序列化就好了。
  • 做了一些额外的挖掘,结果你无法序列化接口。所以我想我在这里坚持使用Collection&lt;string&gt;...
  • @Koen,我担心你是对的。您可以进行自定义序列化,或在您自己的类上实现ICollection。两者都可以视为矫枉过正。
  • 确实如此。但由于我在这里有点时间压力,我会坚持收藏。感谢您的宝贵时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-22
  • 1970-01-01
相关资源
最近更新 更多