Net Core:Static 类 是否需要来自 SonarQube 的 ISerializable?

Net Core: Do Static Classes need ISerializable from SonarQube?

有人了解 Sonarqube 中的这个错误吗?我们想要实现一个网格需求维度——给定一些图片。 2张图片是1X2网格,4张图片是2X2,6张图片是3X2等等。Sonarqube要求为这种简单的静态类实现Serializable。我该如何解决这个问题,是否有必要?

当前代码:

public class CardDimensionRequirement:Dictionary<int, CardDimensionRequirementLine> 
{
    public void AddItem(int PictureCount, int Length, int Height, int BootstrapDimension)
    {
        base.Add(PictureCount, new CardDimensionRequirementLine(PictureCount, Length, Height, BootstrapDimension));
    }

    public int GetMaxKey()
    {
        return base.Keys.Max();
    }    
}

static class CardDimensionRequirementSpecs
{
    public static CardDimensionRequirement cardDimensionRequirementData;

    static CardDimensionRequirementSpecs()
    {
        //int PictureCount, int Length, int Height, int BootstrapDimension
        cardDimensionRequirementData = new CardDimensionRequirement();
        cardDimensionRequirementData.AddItem(1, 1, 1, 1);
        cardDimensionRequirementData.AddItem(2, 2, 1, 6);
        cardDimensionRequirementData.AddItem(3, 3, 1, 4);
        cardDimensionRequirementData.AddItem(4, 2, 2, 6);
        cardDimensionRequirementData.AddItem(5, 3, 2, 4);
        cardDimensionRequirementData.AddItem(6, 3, 2, 4);
    }
}

SonarQube推荐:

The ISerializable interface is the mechanism to control the type serialization process. If not implemented correctly this could result in an invalid serialization and hard to detect bugs.

This rules raises an issue on types that implement ISerializable without following the serialization pattern recommended by Microsoft.

Specifically this rule checks for these problems:

The System.SerializableAttribute attribute is missing.
Non-serializable fields are not marked with the System.NonSerializedAttribute attribute.
There is no serialization constructor.
An unsealed type has a serialization constructor that is not protected.
A sealed type has a serialization constructor that is not private.
An unsealed type has a ISerializable.GetObjectData that is not both public and virtual.
A derived type has a serialization constructor that does not call the base constructor.
A derived type has a ISerializable.GetObjectData method that does not call the base method.
A derived type has serializable fields but the ISerializable.GetObjectData method is not overridden.

Noncompliant Code Example
public class Foo : ISerializable // Noncompliant the [Serializable] attribute is missing
{
}
or

public class Bar
{
}

[Serializable]
public class Foo : ISerializable // Noncompliant the serialization constructor is missing
{
    private readonly Bar bar; // Noncompliant the field is not marked with [NonSerialized]
}

Compliant Solution
public class Bar
{
}

[Serializable]
public class Foo : ISerializable
{
    [NonSerialized]
    private readonly Bar bar;

    public Foo()
    {
        // ...
    }

    protected Foo(SerializationInfo info, StreamingContext context)
    {
        // ...
    }

    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        // ...
    }
}

[Serializable]
public sealed class SubFoo : Foo
{
    private int val;

    public SubFoo()
    {
        // ...
    }

    private SubFoo(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        // ...
    }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        // ...
    }
}
Exceptions
Classes in test projects are not checked.

SonarQube 可能会给出该错误,因为您的基本类型 Dictionary<TKey,TValue>, implements custom binary serialization via the ISerializable 接口。鉴于您的基类型实现了自定义序列化,SonarQube 似乎假设您需要重写该自定义序列化以添加派生类型的声明成员的序列化。

但是,您的类型没有声明自己的字段或属性,因此没有任何特定的序列化内容。

那么,您有哪些解决问题的选择?

如果您不关心二进制序列化并且不需要支持它,您可以采用decorator pattern并实现IDictionary<int, CardDimensionRequirementLine>而不是从 Dictionary<TKey,TValue> 派生。然后,在 CardDimensionRequirement 里面有一些私有字典来进行实际的查找:

public class CardDimensionRequirement : IDictionary<int, CardDimensionRequirementLine>
{
    readonly Dictionary<int, CardDimensionRequirementLine> dictionary = new Dictionary<int, CardDimensionRequirementLine>();

    public void AddItem(int PictureCount, int Length, int Height, int BootstrapDimension)
    {
        Add(PictureCount, new CardDimensionRequirementLine(PictureCount, Length, Height, BootstrapDimension));
    }

    public int GetMaxKey()
    {
        return Keys.Max();
    }   

    #region IDictionary<int,CardDimensionRequirementLine> Members

    // Delegate everything to this.dictionary:

    public void Add(int key, CardDimensionRequirementLine value)
    {
        this.dictionary.Add(key, value);
    }

    // Remainder snipped

不要将 class 标记为 [Serializable] 或实施 ISerializable

这个实现的一个优点是切换到不同的字典,比如 SortedDictionary<int, CardDimensionRequirementLine>,不会是一个破坏性的变化。

如果您确实关心二进制序列化,您应该将您的类型标记为 [Serializable],像这样覆盖 GetObjectData(), and introduce your own streaming constructor

[Serializable]
public class CardDimensionRequirement : Dictionary<int, CardDimensionRequirementLine>
{
    public CardDimensionRequirement() : base() { }

    protected CardDimensionRequirement(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        // Nothing to do since your class currently has no fields
    }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        // Deserialize fields here, if you ever add any.
    }

    // Remainder snipped

作为替代方案,因为您的 CardDimensionRequirement 实际上没有任何自己的数据要记住,您可以简单地使用任何旧的 Dictionary<int, CardDimensionRequirementLine>并将您的方法实现为扩展方法:

public static class CardDimensionRequirementExtensions
{
    public static void AddItem(this IDictionary<int, CardDimensionRequirementLine> dictionary, int PictureCount, int Length, int Height, int BootstrapDimension)
    {
        if (dictionary == null)
            throw new ArgumentNullException();
        dictionary.Add(PictureCount, new CardDimensionRequirementLine(PictureCount, Length, Height, BootstrapDimension));
    }

    public static int GetMaxKey(this IDictionary<int, CardDimensionRequirementLine> dictionary)
    {
        if (dictionary == null)
            throw new ArgumentNullException();
        return dictionary.Keys.Max();
    }   
}