C# CA2227的解决方案或更好的方法?

C# CA2227的解决方案或更好的方法?,c#,visual-studio,code-analysis,C#,Visual Studio,Code Analysis,我只使用代码分析来清理、组织和确保对特定警告的所有实例全局执行这些更改。我进入决赛了,是CA2227 CA2227集合属性应为只读更改“”为 通过删除属性设置程序实现只读 注:这用于EDI文档的映射。这些类表示EDI文档的全部或部分 公共类PO1Loop { public SegmentTypes.PO1LoopSegmentTypes.PO1 PO1{get;set;} 公共集合PIDRepeat1{get;set;} 公共集合2{get;set;} public SegmentTypes.P

我只使用代码分析来清理、组织和确保对特定警告的所有实例全局执行这些更改。我进入决赛了,是CA2227


CA2227集合属性应为只读更改“”为 通过删除属性设置程序实现只读


注:这用于EDI文档的映射。这些类表示EDI文档的全部或部分

公共类PO1Loop
{
public SegmentTypes.PO1LoopSegmentTypes.PO1 PO1{get;set;}
公共集合PIDRepeat1{get;set;}
公共集合2{get;set;}
public SegmentTypes.PO1LoopSegmentTypes.PO4 PO4{get;set;}
/*最大使用量:8*/
公共集合ACKRepeat{get;set;}
}

你可以看到所有的集合属性都会给我这个警告,而且有数百个。当使用上面的类时,我实例化它,没有任何数据。然后在外部添加数据,并通过其公共访问器设置每个单独的变量。我没有使用构造函数方法来实例化这个类,并准备和传递所有数据(对于这些数据的大小,IMO很容易对眼睛造成破坏)。完成并指定所有属性后,整个类将用于生成它所表示的文档的该部分


我的问题是,对于上面描述的用法,正确设置它的更好方法是什么?我是保留公共访问器并完全禁止此警告,还是有一个完全不同的解决方案可以工作?

下面是关于错误的说明,以及如何避免错误


以下是我对这个问题的看法


考虑以下类别:

class BigDataClass
{
    public List<string> Data { get; set; }
}

这段代码是非常有效的,事实上是推荐的方法。为什么?因为
列表是对包含剩余数据的内存位置的引用


现在,您唯一不能做的就是直接设置
数据
属性。即,以下内容无效:

var bigData = new BigDataClass();
bigData.Data = new List<string>();

我们可能需要
一些字符串,但我们会得到
字符串1
。这也意味着您无法可靠地将事件附加到相关的
集合
,因此无法可靠地确定是添加了新值还是删除了值


可写集合属性允许用户使用完全不同的集合替换集合


本质上,如果您只需要运行构造函数或赋值一次,则省略
set
修饰符。您不需要它,直接分配集合是违反最佳实践的


现在,我并不是说永远不要在
集合上使用setter,有时您可能需要一个setter,但通常不应该使用它们


您始终可以使用
.AddRange
.Clone
等。在
集合
上,您只会失去直接分配的能力

系列化
最后,如果我们希望
序列化
反序列化
一个包含
集合
但没有
集合的类,我们该怎么办?好的,通常有不止一种方法,最简单的方法(在我看来)是创建一个表示序列化集合的
属性


以我们的
BigDataClass
为例。如果我们希望
序列化
,然后
用以下代码反序列化该类,则
数据
属性将没有元素

JavaScriptSerializer jss = new JavaScriptSerializer();
BigDataClass bdc = new BigDataClass();
bdc.Data.Add("Test String");
string serd = jss.Serialize(bdc);
Console.WriteLine(serd);
BigDataClass bdc2 = jss.Deserialize<BigDataClass>(serd);

另一个选项始终是
DataContractSerializer
(通常这是一个更好的选项)。您可以在上找到有关它的信息。

感谢@Matthew、@CraigW和@EBrown帮助我理解此警告的解决方案

public class PO1Loop
{

    public SegmentTypes.PO1LoopSegmentTypes.PO1 PO1 { get; set; }

    public Collection<SegmentTypes.PO1LoopSegmentTypes.PID1> PIDRepeat1 { get; private set; }

    public Collection<SegmentTypes.PO1LoopSegmentTypes.PID2> PIDRepeat2 { get; private set; }

    public SegmentTypes.PO1LoopSegmentTypes.PO4 PO4 { get; set; }

    /* Max Use: 8 */
    public Collection<SegmentTypes.PO1LoopSegmentTypes.ACK> ACKRepeat { get; private set; }

    public PO1Loop()
    {
        PIDRepeat1 = new Collection<SegmentTypes.PO1LoopSegmentTypes.PID1>();
        PIDRepeat2 = new Collection<SegmentTypes.PO1LoopSegmentTypes.PID2>();
        ACKRepeat = new Collection<SegmentTypes.PO1LoopSegmentTypes.ACK>();
    }

}
公共类PO1Loop
{
public SegmentTypes.PO1LoopSegmentTypes.PO1 PO1{get;set;}
公共集合PIDREAT1{get;private set;}
公共集合2{get;private set;}
public SegmentTypes.PO1LoopSegmentTypes.PO4 PO4{get;set;}
/*最大使用量:8*/
公共集合{get;private set;}
公共PO1Loop()
{
PIDRepeat1=新集合();
PIDRepeat2=新集合();
ACKRepeat=新集合();
}
}

当想要将数据分配给集合类型时,请使用AddRange、Clear或任何其他方法的变体来修改集合。

我必须修复一些CA2227冲突,因此我必须将“readonly”关键字添加到集合字段,当然,还必须删除setter属性。一些使用setter的代码刚刚创建了一个新的集合对象,它最初是空的。这段代码肯定没有编译,所以我不得不添加一个SetXxx()方法来实现缺少的setter功能。我是这样做的:

public void SetXxx(List<string> list)
{
    this.theList.Clear();
    this.theList.AddRange(list);
}
public void SetXxx(列表)
{
this.theList.Clear();
this.theList.AddRange(列表);
}

使用setter的调用方代码已替换为对方法SetXxx()的调用


现在,现有列表将被清除并填充来自另一个列表的新项目,而不是创建一个完整的新列表,并作为参数传入。由于原始列表是只读的,并且只创建了一次,因此它将始终保留


我相信这也是避免垃圾收集器必须删除超出范围的旧对象的一个好方法,第二,创建新的收集对象,尽管已经有一个。

使用当前的VS2019,我们可以简单地做到:

public List<string> Data { get; } = new List<string>();

涵盖解决CA2227错误的所有可能方案: 这涵盖了使用实体框架时的实体关系映射

class Program
{

    static void Main(string[] args)
    {
        ParentClass obj = new ParentClass();

        obj.ChildDetails.Clear();
        obj.ChildDetails.AddRange();

        obj.LstNames.Clear();
        obj.LstNames.AddRange();


    }


}
public class ChildClass
{ }
public class ParentClass
{
    private readonly ICollection<ChildClass> _ChildClass;
    public ParentClass()
    {
        _ChildClass = new HashSet<ChildClass>();
    }

    public virtual ICollection<ChildClass> ChildDetails => _ChildClass;
    public IList<string> LstNames => new List<string>();
}
类程序
{
静态void Main(字符串[]参数)
{
ParentClass obj=新的ParentClass();
obj.ChildDetails.Clear();
obj.ChildDetails.AddRange();
obj
public class PO1Loop
{

    public SegmentTypes.PO1LoopSegmentTypes.PO1 PO1 { get; set; }

    public Collection<SegmentTypes.PO1LoopSegmentTypes.PID1> PIDRepeat1 { get; private set; }

    public Collection<SegmentTypes.PO1LoopSegmentTypes.PID2> PIDRepeat2 { get; private set; }

    public SegmentTypes.PO1LoopSegmentTypes.PO4 PO4 { get; set; }

    /* Max Use: 8 */
    public Collection<SegmentTypes.PO1LoopSegmentTypes.ACK> ACKRepeat { get; private set; }

    public PO1Loop()
    {
        PIDRepeat1 = new Collection<SegmentTypes.PO1LoopSegmentTypes.PID1>();
        PIDRepeat2 = new Collection<SegmentTypes.PO1LoopSegmentTypes.PID2>();
        ACKRepeat = new Collection<SegmentTypes.PO1LoopSegmentTypes.ACK>();
    }

}
public void SetXxx(List<string> list)
{
    this.theList.Clear();
    this.theList.AddRange(list);
}
public List<string> Data { get; } = new List<string>();
    public class CA2227TestClass
    {
        public IList Data { get; } = new List<string>();
    }

    [TestMethod]
    public void CA2227_Serialization()
    {
        var test = new CA2227TestClass()
        {
            Data = { "One", "Two", "Three" }
        };

        var json = JsonConvert.SerializeObject(test);

        Assert.AreEqual("{\"Data\":[\"One\",\"Two\",\"Three\"]}", json);

        var jsonObject = JsonConvert.DeserializeObject(json, typeof(CA2227TestClass)) as CA2227TestClass;

        Assert.IsNotNull(jsonObject);
        Assert.AreEqual(3, jsonObject.Data.Count);
        Assert.AreEqual("One", jsonObject.Data[0]);
        Assert.AreEqual("Two", jsonObject.Data[1]);
        Assert.AreEqual("Three", jsonObject.Data[2]);
        Assert.AreEqual(typeof(List<string>), jsonObject.Data.GetType());
    }
class Program
{

    static void Main(string[] args)
    {
        ParentClass obj = new ParentClass();

        obj.ChildDetails.Clear();
        obj.ChildDetails.AddRange();

        obj.LstNames.Clear();
        obj.LstNames.AddRange();


    }


}
public class ChildClass
{ }
public class ParentClass
{
    private readonly ICollection<ChildClass> _ChildClass;
    public ParentClass()
    {
        _ChildClass = new HashSet<ChildClass>();
    }

    public virtual ICollection<ChildClass> ChildDetails => _ChildClass;
    public IList<string> LstNames => new List<string>();
}
class Holder
{
    public IReadOnlyList<string> Col { get; set; } = new List<string>();
}

var list = new List<string> { "One", "Two" };
var holder = new Holder() { Col = list } ;
var json = JsonConvert.SerializeObject(holder);
// output json {"Col":["One","Two"]}
var deserializedHolder = JsonConvert.DeserializeObject<Holder>(json);