你不知道的经常使用 代码分析 规范

visual studio有个功能,代码分析,通常开发完毕后,除了处理常规的“错误列表”显示的“错误”和“警告”,咱们更加应该注意的是,运行代码分析功能,规范咱们的代码,由于很差的编码习惯,在没有人指出和没有团队氛围的开发中,不少时候都是一路不规范到底html

visual studio菜单的“分析”-》“对***运行代码分析”或者在解决方案的类库右击选择代码分析
若是为了强迫本身养成良好的c#微软规范的习惯,咱们能够右击类库属性,找到最后一行标签“代码分析”,并在对应右侧明细的“规则集”->"运行此规则集"下拉框中选择Microsoft的全部规则。固然若是你须要每次生成代码时让vs自动帮咱们执行代码分析,也能够勾选上复选框“生成时启动代码分析”,在长时间的编码中若是每次都运行代码分析,咱们的代码会愈来愈规范和高效率
我找了之前的不少代码和网上下载的代码,以及公司的一些朋友的代码,逐一代码分析后,总结了以下常规开发中通常会遇到的规范问题,这些都不是错误或者警告,可是对于须要提升自身修养的程序员来讲,这是必修课,固然本文只是抛砖引玉,更多的规范在微软的官方文档中都有,只是不少永远不会遇到
MSDN:http://msdn.microsoft.com/zh-cn/library/dd264939(v=vs.100).aspx

下面我将最最最经常使用的规范问题,总结在一段程序当中(至关简单的程序),朋友们能够不运行代码分析凭借本身的经验来判断,到底有多少处不规范的地方
我敢保证,对于常规的要求不是很严格的开发,如下这些问题或多或少都会在您的代码中出现
调用入口:程序员

 1 static void Main(string[] args)
 2         {
 3             try
 4             {
 5                 Class_Test test1 = new Class_Test();
 6                 test1.Fun1();
 7             }
 8             catch (Exception ex)
 9             {
10                 Console.WriteLine(ex.ToString());
11             }
12             Console.ReadLine();
13         }

                                                                                     (代码1)web

核心代码(为了查看方便把多个类放到同一个文件):数据库

 1 namespace TestBLL.Class_Test
 2 {
 3     public class Class_Test
 4     {
 5         public void Fun1()
 6         {
 7             const int param1 = 10;
 8             string name = param1.ToString();
 9             if (name == "")
10             {
11                 Console.WriteLine("empty");
12             }
13             else
14             {
15                 try
16                 {
17                     Class_Test2 test2 = new Class_Test2();
18                     test2.Age = 25;
19                     Fun_Test1("", ref test2, 100, "");
20 
21                     bool param2 = Boolean.Parse(name);
22                     Console.WriteLine(param2);
23                     List<string> list = new List<string>();
24                     list.Add(name);
25                 }
26                 catch (Exception ex)
27                 {
28                     Console.WriteLine(ex.Message);
29                     throw ex;
30                 }
31             }
32         }
33         public void Fun_Test1(string param1, ref  Class_Test2 param2, int param3, string Param4)
34         {
35             string param5 = "";
36             param2.Age = 24;
37             param2.Fun1(param1, Param4, ref param5);
38 
39         }
40     }
41     public class Class_Test2
42     {
43         public int Age { get; set; }
44         public string getTimeType()
45         {
46             string TimeType = string.Empty;
47             int hour = DateTime.Now.Hour;
48             if (hour >= 1 && hour < 5)
49                 TimeType = "凌晨";
50             else if (hour >= 5 && hour < 11)
51                 TimeType = "早上";
52             return TimeType;
53         }
54         public void Fun1(string param, string param1, ref string param2)
55         {
56             # region
57             for (int i = 0; i < 10; i++)
58             {
59                 string name = "yhc";
60                 Console.WriteLine(name);
61             }
62             //此处省略99个如上for循环代码块
63             #endregion
64             Fun2();
65             Console.WriteLine(param);
66         }
67         private void Fun2()
68         {
69             Console.WriteLine();
70         }
71     }
72     public class Class_Test3
73     {
74         public static void Fun1()
75         {
76         }
77     }
78     public struct StructTest<T>
79     {
80         public List<T> rows;
81     }
82 }

                                                                            (代码2)编程

代码分析后有N个警告,大多数都要引发重视
一、CA2210 程序集应具备有效的强名称 用强名称密钥对 'TestBLL.dll' 进行签名。
  
c#

二、CA1014 用 CLSCompliantAttribute 标记程序集 使用 CLSCompliant(true)来标记 'TestBLL.dll',由于它公开外部可见的类型。数组

  • 若是您的程序集须要跨编程语言使用,而且您知道cls(公共语言规范)的概念,那么您必须处理这条警告,由于必须 CLSCompliantAttribute 显式指示 CLS 符合性,让您的程序集在跨语言使用时不会出现冲突或者异常,好比vb中函数是不区分大小写,若是您不处理这条警告,在编写您的程序集时,您在代码同一个Class中有两个方法,只是大小写不一样,例如Fun和fun,那么不会有任何警告和问题,可是被vb中调用就会报异常
  • 若是您须要使用CLSCompliantAttribute 来标记程序集,只须要在AssemblyInfo.cs文件中
    添加命名空间引用using System;并加上代码[assembly:CLSCompliant(true)]便可
    详细参考地址:http://www.cnblogs.com/mywebname/articles/598460.html
    http://msdn.microsoft.com/zh-cn/library/ms182156
  • 若是你以为没有必要,能够在“操做”中选择“禁止显示此消息”

三、 CA1709 标识符的大小写应当正确 更正程序集名称 'TestBLL.dll' 中“BLL”的大小写,将其改成“Bll”。(命名空间、类名等都是如此)   服务器

  • 咱们都知道Camel和Pascal命名规则,也会常常按照此规则编写代码,可是仍是会常常写出相似TestBLL的命名代码,这边不少人忽略的是双字母单词,通常的vs分析会检测您的命名中是否存在双字母单词(双字母单词固定就那么多),若是存在则不会报错好比:采用Pascal命名规范给程序集命名TestDB,那么不会发出警告,由于DB是属于双字母单词,若是采用Pascal命名规则,那么通常三个或者三个以上字符是不容许出现所有大写的,好比这里的TestBLL中的"BLL"
  • 详细参考地址:http://msdn.microsoft.com/query/dev12.query?appId=Dev12IDEF1&l=ZH-CN&k=k(CA1709);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5)&rd=true

四、CA1707 标识符不该包含下划线 从命名空间名称“TestBLL.Class_Test”中移除下划线。app

  • 在.NET平台下,微软不建议使用匈牙利命名法,因此在这里有下划线的存在也会抛出警告,后面的类名Class_Test二、Class_Test3,以及方法名Fun_Test1都是不规范的
  • 详细参考地址:http://msdn.microsoft.com/library/ms182245(VS.100).aspx

五、 CA1724 类型名不该与命名空间冲突 类型名 'ClassTest' 与命名空间名称“TestBLL.ClassTest”总体或部分冲突。请更改其中任一名称以消除冲突。编程语言

六、CA1305 指定 IFormatProvider 因为 'int.ToString()' 的行为可能会因当前用户的区域设置不一样而不一样,请将 'ClassTest.Fun1()' 中的此调用替换为对 'int.ToString(IFormatProvider)' 的调用。若是要向用户显示 'int.ToString(IFormatProvider)' 的结果,请指定 'CultureInfo.CurrentCulture' 做为“IFormatProvider”参数。或者,若是软件将存储和访问此结果(例如,当将此结果保留到磁盘或数据库中时),则指定 'CultureInfo.InvariantCulture'。

  • 代码位置:代码2中的8行,string name = param1.ToString();
  • 在数据具备逻辑性判断或者存储数据的时候,一些区域化参数必定不要忘记,几乎全部时候咱们都是基本本国开发,历来不会考虑这些参数,包括ToString(),ToUpper(),DateTime.ToShortDateString(),DateTime.ToString()等等,因此也历来没有问题,那是由于系统会选择你当前线CultureInfo(和你强制加参数CultureInfo.CurrentCulture效果同样)。虽然用户界面UI看上去很智能,可是带来的一个问题是:若是你的数据是纯逻辑性判断或者存储的时候,请不要偷懒,由于逻辑判断的值或者存储起来的值是须要一个固定的不须要随着区域而变化的值,不然服务端将各类异常奇怪现象,此时就要设置'CultureInfo.InvariantCulture',由于它是不依赖于区域性(固定)的 System.Globalization.CultureInfo 对象
  • 详细参考地址:http://weishangxue.blog.163.com/blog/static/2157518820117193125196/
  • 修改成:string name = param1.ToString(CultureInfo.InvariantCulture);

七、CA1820 使用字符串长度测试是否有空字符串 使用“String.IsNullOrEmpty”调用来替换 'ClassTest.Fun1()' 中的 'string.operator ==(string, string)' 调用。

  • 代码未知:代码2中的9行,if (name == "" )
  • 判断字符串是否为空,请不要再用equals 或者== “”了,由于字符串的equal或者==(操做符重载)都是重载过的,object的equal或者==都是比较是否引用相同,而string重载后则是比较值了,首先能够肯定的是String.IsNullOrEmpty或者String.Length确定比equals或者==“”效率高,由于用ILDASM查看il代码时,发现执行的il代码行数前者比后者少,这是其次,最重要的是用ILSpy反编译String类的Equals代码后发现,内部作了太多操做
1 public bool Equals(string value)
2         {
3             if (this == null)
4             {
5                 throw new NullReferenceException();
6             }
7             return value != null && (object.ReferenceEquals(this, value) || (this.Length == value.Length && string.EqualsHelper(this, value)));
8         }
1 public static bool IsNullOrEmpty(string value)
2         {
3             return value == null || value.Length == 0;
4         }

 

八、CA2200 再次引起以保留堆栈详细信息 'ClassTest.Fun1()' 再次引起捕获的异常并将其显式地指定为一个参数。请改用不带参数的“throw”以保留该异常最初引起时所在的堆栈位置。

  • 代码位置:代码2中的29行,throw ex;
  • 这边有两个问题,第一:捕捉异常最好不用直接用Exception类型来捕捉,由于这样系统会遍历全部异常信息来查找匹配的异常,致使的后果就是一旦发生异常,系统就会卡顿好久,最好的作法是由上到下先捕捉可能会发生的异常类型,最后再捕捉异常类型基类Exception,常见的异常类并非不少,若是有时候您根本不知道你应该写什么异常类型,那么您先保存下经常使用的异常类列表,使用时再去查看,时间长了天然就知道了,以下:
    C#异常类1、基类Exception
    
    C#异常类2、常见的异常类
    
    1、SystemException类:该类是System命名空间中全部其余异常类的基类。(建议:公共语言运行时引起的异常一般用此类)
    
    2、ApplicationException类:该类表示应用程序发生非致命错误时所引起的异常(建议:应用程序自身引起的异常一般用此类)
    
    C#异常类3、与参数有关的异常类
    
    此类异常类均派生于SystemException,用于处理给方法成员传递的参数时发生异常
    
    1、ArgumentException类:该类用于处理参数无效的异常,除了继承来的属性名,此类还提供了string类型的属性ParamName表示引起异常的参数名称。
    
    2、FormatException类:该类用于处理参数格式错误的异常。
    
    C#异常类4、与成员访问有关的异常
    
    1、MemberAccessException类:该类用于处理访问类的成员失败时所引起的异常。失败的缘由可能的缘由是没有足够的访问权限,也多是要访问的成员根本不存在(类与类之间调用时经常使用)
    
    2、MemberAccessException类的直接派生类:
    
    i、FileAccessException类:该类用于处理访问字段成员失败所引起的异常
    
    ii、MethodAccessException类:该类用于处理访问方法成员失败所引起的异常
    
    iii、MissingMemberException类:该类用于处理成员不存在时所引起的异常
    
    C#异常类5、与数组有关的异常
    
    如下三个类均继承于SystemException类
    
    1、IndexOutOfException类:该类用于处理下标超出了数组长度所引起的异常
    
    2、ArrayTypeMismatchException类:该类用于处理在数组中存储数据类型不正确的元素所引起的异常
    
    3、RankException类:该类用于处理维数错误所引起的异常
    
    C#异常类6、与IO有关的异常
    
    1、IOException类:该类用于处理进行文件输入输出操做时所引起的异常。
    
    2、IOException类的5个直接派生类:
    
    i、DirectionNotFoundException类:该类用于处理没有找到指定的目录而引起的异常。
    
    ii、FileNotFoundException类:该类用于处理没有找到文件而引起的异常。
    
    iii、EndOfStreamException类:该类用于处理已经到达流的末尾而还要继续读数据而引起的异常。
    
    iv、FileLoadException类:该类用于处理没法加载文件而引起的异常。
    
    v、PathTooLongException类:该类用于处理因为文件名太长而引起的异常。
    
    C#异常类7、与算术有关的异常
    
    1、ArithmeticException类:该类用于处理与算术有关的异常。
    
    2、ArithmeticException类的派生类:
    
    i、DivideByZeroException类:表示整数货十进制运算中试图除以零而引起的异常。
    
    ii、NotFiniteNumberException类:表示浮点数运算中出现无穷打或者非负值时所引起的异常。
    异常类型

     第二:请不要throw ex,让外部代码再去Catch,而应该是throw,再让外部代码再去Catch,其实最好的作法是throw new Exception(ex.Message, ex),如下用ex.ToString()输出异常明细(用于log保存或者异常明细跟踪):

    throw ex:抛出新异常,重置堆栈原始异常点,在外部捕捉时根本查看不到具体异常行

    System.FormatException: 该字符串未被识别为有效的布尔值。
       在 TestBll.ClassTest.Fun1() 位置 e:\测试\console\ConsoleApplication1\TestBLL\Class1.cs:行号 38
       在 ConsoleApplication1.Program.Main(String[] args) 位置 e:\测试\console\ConsoleApplication1\ConsoleApplication1\Program.cs:行号 18


    throw:抛出异常,外部捕捉时能够看到具体异常行

    System.FormatException: 该字符串未被识别为有效的布尔值。
       在 System.Boolean.Parse(String value)
       在 TestBll.ClassTest.Fun1() 位置 e:\测试\console\ConsoleApplication1\TestBLL\Class1.cs:行号 38
       在 ConsoleApplication1.Program.Main(String[] args) 位置 e:\测试\console\ConsoleApplication1\ConsoleApplication1\Program.cs:行号 18


    throw new Exception(ex.Message, ex):新建一个异常,而后将ex的堆栈异常信息做为其内部异常(innerException)

    System.Exception: 该字符串未被识别为有效的布尔值。 ---> System.FormatException: 该字符串未被识别为有效的布尔值。
       在 System.Boolean.Parse(String value)
       在 TestBll.ClassTest.Fun1() 位置 e:\测试\console\ConsoleApplication1\TestBLL\Class1.cs:行号 29
       --- 内部异常堆栈跟踪的结尾 ---
       在 TestBll.ClassTest.Fun1() 位置 e:\测试\console\ConsoleApplication1\TestBLL\Class1.cs:行号 37
       在 ConsoleApplication1.Program.Main(String[] args) 位置 e:\测试\console\ConsoleApplication1\ConsoleApplication1\Program.cs:行号 18

    仔细观察你会发现,第一种根本找不到具体异常的行,第二种虽然有具体异常信息,可是没有行号,而第三种是最完善的,有具体异常信息,还有具体异常行号

  • 详细参考地址:http://blog.csdn.net/honkerhero/article/details/1699093
    http://www.cnblogs.com/JerryTian/archive/2012/09/24/2699459.html
  • 修改成:
                   try{
                    }
                    catch (FormatException ex)
                    {
                        Console.WriteLine(ex.Message);
                        throw new FormatException(ex.Message, ex);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        throw new Exception(ex.Message, ex);
                    }

 九、CA1801 检查未使用的参数 从未用过 'ClassTest.FunTest1(string, ref ClassTest2, int, string)' 的参数 'param3'。请移除该参数或在方法体中

  • 代码位置:代码2中的33行,public void Fun_Test1(string param1, ref  Class_Test2 param2, int param3, string Param4)
  • 这个不用多说,就是历来没有使用过的参数或者变量,都删除掉,通常你们不会特地的出现这种状况,可是每每在传递参数特别多,加上往复的逻辑变动后,每每会忘记删掉不用的参数,或者说赶进度,根本不会去仔细看哪些没用的没删,最多见的状况就是,书写代码时写着写着,忽然发现好像有另外一种更方便的写法,而后没有删除干净以前的逻辑
  • 修改成:public void FunTest1(string param1, ref  ClassTest2 param2, string Param4)


十、CA1709 标识符的大小写应当正确 在成员 'ClassTest.FunTest1(string, ref ClassTest2, string)' 中,更正参数名称 'Param4' 中“Param”的大小写,将其改成“param”。

  • 代码位置:代码2中的33行,public void Fun_Test1(string param1, ref  Class_Test2 param2, int param3, string Param4)的Param4
  • 这个不用多说,命名规范看上面第3点
  • 修改成:public void FunTest1(string param1, ref  ClassTest2 param2, string param4)

 十一、CA1045 不要经过引用来传递类型 考虑使用不须要将 'param2' 做为引用参数的设计。

  • 代码位置:代码2中的33行,public void Fun_Test1(string param1, ref  Class_Test2 param2, int param3, string Param4)的Param4
  • 对于引用类型做为参数传递时,其自己就是传递对象的引用(其实我更愿意说是对象在堆的在内存中的地址),而Fun_Test1方法中对于Class_Test2类型的对象只是改变其属性值(param2.Age = 24;)传递过来的指针指向的堆中的对象的首地址并无发生变化,因而对传递后的对象作任何操做均可以影响传入以前的Class_Test2对象(前提是不new一块内存来指向,或者指向别的内存块),即在执行完毕后在外部Age也会变成24,这里用了ref是画蛇添足,由于对于引用类型若是还用ref的话,那么传递的是该对象引用的引用(其实我更愿意说是该对象在堆中内存地址的引用,即变量在栈中的地址),对于当前代码的逻辑操做,是不须要这么作的,由于在Fun_Test1方法中处理传入的对象的时候并无从新new一个对象,若是Fun_Test1方法中代码变成:
    1             string param5 = "";
    2             param2 = new ClassTest2();
    3             param2.Age = 24;
    4             param2.Fun1(param1, param4, ref param5);     

    那么是时候考虑用ref了

  • 详细参考地址:http://msdn.microsoft.com/library/ms182146(VS.100).aspx
    http://msdn.microsoft.com/zh-cn/library/s6938f28.aspx
  • 修改成:public void FunTest1(string param1, ref  ClassTest2 param2, string param4)

 

 十二、CA1062 验证公共方法的参数 在外部可见方法 'ClassTest.FunTest1(string, ref ClassTest2, string)' 中,请先验证局部变量“'(*param2)'”,而后再使用它,该变量是从参数“param2”从新分配而来的。

  • 代码位置:代码2中的36行,param2.Age = 24
  • 引用类型的局部变量在使用前必须验证是否为空等,防止发生异常
  • 修改成:添加if (param2 != null){}判断

1三、CA1024 在适用处使用属性 若是可行,请将 'Class_Test2.getTimeType()' 改成属性
       CA1822 将成员标记为 static 从未使用 'Class_Test2.getTimeType()' 的“this”参数(Visual Basic 中为“Me”)。根据须要,将成员标记为 static (Visual Basic 中为“Shared”),或者在方法体或至少一个属性访问器中使用“this”/“Me”。

  • 代码位置:代码2中的44行,public string getTimeType()
  • 仔细观察:这种代码在一个类中太常见了,以致于咱们都写得飞起,编译器代码分析提示咱们是否能够将其改成属性或者静态方法是有缘由的
     1  public string getTimeType()
     2         {
     3             string TimeType = string.Empty;
     4             int hour = DateTime.Now.Hour;
     5             if (hour >= 1 && hour < 5)
     6                 TimeType = "凌晨";
     7             else if (hour >= 5 && hour < 11)
     8                 TimeType = "早上";
     9             return TimeType += "";
    10         }

    改成属性的缘由
    第一该方法getTimeType修饰符是public,而且没有采用任何参数或返回的值不是数组,这符合属性的要求,同时属性大多数状况下表明的是数据,方法表明的是执行操做,属性在访问更加方便,若是您的所谓的方法逻辑处理不是不少,不是很耗时,而且永远不会由于调用次数的不一样而产生不一样的结果,那么为何不优先使用属性呢?
    改成静态方法的缘由:能够将不访问实例数据或不调用实例方法的成员标记为 static,getTimeType内部没有访问任何其余类实例的属性或者实例方法,也没有访问当前所属类的属性或者字段等数据(没有使用this),那么它就是符合抽象类的条件,这里不讨论静态方法和实例方法性能对比问题,只是代码规范上说这个getTimeType方法不该该定义成实例方法,若是你把这样一个十分经常使用的实例方法(其余类也会用调用该方法)随便定义在了一个类中,那么你软件的设计将是十分失败,耦合度过高,牵一发而动全身,而且从面向对象的方面来说,一个对象很是通用的功能为什么不定义成对象的基类的方法(若是全部调用类都只是从一个基类继承)或者定义成公共类的静态方法(若是全部调用类南辕北辙)

 1四、CA1045 不要经过引用来传递类型 考虑使用不须要将 'param2' 做为引用参数的设计。

  • 代码位置:代码2中的54行, public void Fun1(string param, string param1, ref string param2)
  • 这条在第11条中已经描述过,但情景不一样,我认为该警告并不科学,能够忽略,在咱们真的有这种需求ref 取得改变后的string类型的值时,用ref是能够的,由于string虽然做为引用类型,传递的是对象在堆中的地址,可是若是我改变param2的值,熟悉string机制的朋友知道,这时候会从新在堆中分配一块内存,而后param2再指向这块心的内存,到这里为止,所作的操做都和外部的那个string对象没有任何关系,那么使用ref后,传递的是该对象堆中地址的引用(通俗理解为对象在栈的地址,),那么你对param2所作的任何操做,其实都是在对外部的那个string对象操做,就算你再new出一块堆上的内存
  • 不用修改,忽略代码提示

 

1五、CA1053 静态容器类型不该具备构造函数 因为类型 'Class_Test3' 仅包含“static”成员,所以将它标记为“static”

  • 代码位置:代码2中的72行, public class Class_Test3
  • 当前类所有都是静态方法,为什么类型要定义成实例类?这样在编译时反而编译器还会为你生成默认的构造函数,这是没有必要的,同时因为类型没有一个非静态成员,即便建立实例也没法提供对类型的任何成员的访问
  • 修改成:public static class Class_Test3

 

1六、CA1815 重写值类型上的 Equals 和相等运算符 'StructTest1<T>' 应重写相等(==)和不等(!=)运算符。

  • 代码位置:代码2中的78行, public struct StructTest<T>
  • 首先您定义的结构(值类型)的代码中有没有用到两个结构对象比较的地方,若是没有,则忽略这条提示,若是有,那么您必须重写Equals等运算符, 对于值类型,Equals 的继承的实现使用反射库,并比较全部字段的内容。 反射须要消耗大量计算资源,可能没有必要比较每个字段是否相等。 若是但愿用户对实例进行比较或排序,或者但愿用户将其做为哈希表键,则值类型必须实现 Equals。 若是编程语言支持运算符重载,则还应提供等号和不等号运算符的实现
  • 酌情添加剧写代码,您能够搜索如何重写运算符

 

1七、CA1002 不要公开泛型列表 更改 'StructTest1<T>.rows' 中的 'List<T>' 以使用 Collection<T>、ReadOnlyCollection<T> 或 KeyedCollection<K,V> TestBLL Class1.cs

  • 代码位置:代码2中的79行, public List<T> rows;
  • 这个代码分析提示也让我很纠结,咱们一直用的List<T>怎么就会不规范了,微软说:“System.Collections.Generic.List<T> 是针对性能(而非继承)设计的泛型集合。 System.Collections.Generic.List<T> 不包含更便于更改继承类的行为的虚拟成员。 下面的泛型集合是针对继承功能设计的,应公开为 System.Collections.Generic.List<T> 之外的内容”,在平时的使用中,我的以为好像没有什么太大区别,有人测试过效率也相差无几,并且不少绑定都是须要List<T>才能绑定
    终于找到一篇能解决疑惑的文章:
    刚开始还不太理解这个设计规范的意思,因而查了下资料,在Why we don’t recommend using List<T> in public APIs 一文中,简要介绍了缘由:
    
    List<T>类型并非为可扩展性而设计的,他优化了性能,可是丢失了可扩展性。好比,它没有提供任何能够override的成员。这样就不能得到诸如集合改变时获取通知的功能。Collection<T>集合容许咱们重写受保护的SetItem方法,这样当咱们向集合中添加或者修改集合中的记录,调用SetItem方法的时候就能够自定义一些事件来通知对象。
    List<T>对象有太多的与“场景”不相关的属性和成员,将其做为成员类型对外暴露在一些状况下显得过“重”,好比在WindowsForm中ListView.Items并无返回一个List对象,而是一个 ListViewItemCollection对象,该对象的签名为:
    // Summary:
    //     Represents the collection of items in a System.Windows.Forms.ListView control
    //     or assigned to a System.Windows.Forms.ListViewGroup.
    [ListBindable(false)]
    public class ListViewItemCollection : IList, ICollection, IEnumerable
    是一个实现ICollection接口的对象。
    
    还有在咱们经常使用的DataTable的Rows对象是一个DataRowCollection对象,该对象继承自InternalDataCollectionBase
    
    // Summary:
    //Represents a collection of rows for a System.Data.DataTable.
    public sealed class DataRowCollection : InternalDataCollectionBase
    public class InternalDataCollectionBase : ICollection, IEnumerable
    而InternalDataCollectionBase则实现ICollection接口。
    
    public class InternalDataCollectionBase : ICollection, IEnumerable
    
    能够看到微软的.NET BCL中没有直接暴露List类型的成员。该规则建议咱们使用Collection<T>。
    
    List<T>一般用来做为类的内部实现,由于它对性能进行过优化,具备一些丰富的功能,而Collection<T>则是提供了更多的可扩展性。在编写公共API的时候,咱们应该避免接受或者返回List<T>类型的对象,而是使用List的基类或者Collection接口。
    
    Collection<T>虽然能够直接使用,可是一般做为自定义集合的基类来使用,通常的咱们应该使用Collection<T>类型的对象来对外暴露功能,除非须要一些List<T>中特有的属性。
    Collection和List的主要区别和使用场景

     

  • 酌情添加剧写代码,您能够搜索如何重写运算符
  • 在此给出连接:http://www.cnblogs.com/yangecnu/archive/2014/05/23/Do-not-expose-generic-lists.html
    http://www.cnblogs.com/netlife/archive/2009/05/21/1271597.html
    http://msdn.microsoft.com/library/ms182142(VS.100).aspx

 

1八、CA1051 不要声明可见实例字段 因为字段 'StructTest1<T>.rows' 在其声明类型的外部可见,所以,请将它的可访问性改成私有,并添加一个与该字段当前的可访问性相同的属性以提供对该属性的访问。

  • 代码位置:代码2中的79行, public List<T> rows;
  • 永远不要声明公开的字段,面向对象语言的封装性决定,若是要公开,请用属性
  • 修改成属性
相关文章
相关标签/搜索