C#要点

2021-04-24 12:27

阅读:545

标签:upper   instr   switch   增加   protected   结果   控制   托管代码   abc   

内容摘要

 

1 数据类型

  1.1表达范围问题

  1.2 数组的长度问题

  1.3 值类型与引用类型

  1.4 匿名类型与隐式类型

  1.5硬编码造成的精度丢失问题

2 控制流语句

  2.1 foreach语句

  2.2 if-else语句

  2.3 for循环

  2.4 Switch语句

3

  3.1 配置文件中使用静态字段或静态属性 6

  3.2 虚方法中不要含有业务逻辑

  3.3 指定构造器

  3.4 对比virtualabstract

  3.5默认初始值

  3.6 readonly

  3.7 构造函数的调用问题

  3.8 静态类和静态成员

  3.9 可访问性

  3.10尽量使用属性而不是字段

  3.11 接口中的属性

4 泛型

  4.1string.Join方法不能识别泛型

集合

  5.1 关于集合的标准查询运算符

  5.2 提高集合插入性能

6 异常处理

  6.1 优先考虑在最外层捕获异常

  6.2 try...finallyreturn

7 扩展

  7.1System.Linq的行为进行扩展

 

1 数据类型

1.1表达范围问题

int类型只能表达-232 232 -1范围内的数据,floatdoubleint范围要宽的多,所以在使用这些类型时注意挑选合适的类型使用,另外与金融计算相关时使用decimal

1.2 数组的长度问题

定义一个数组时最大的长度是多少?理论上最大长度为int.MaxValue。对于32位有符号整数来说最大值为214748364764位有符号整数最大值为9223372036854775807。一般来讲不会定义太长的数组,因为这样会比较消耗内存。

 

1.3 值类型与引用类型

string为引用类型,但下面的方法不会改变其自身:

string str =”abc”;

str.ToUpper();

上面的代码不会将其转为大写,下面写法可行:

str = str.ToUpper();

1.4 匿名类型与隐式类型

C#的匿名类型没有名称,是由编译器动态生成的数据类型,但它仍然是强类型。对匿名类型来说,不可能指定数据类型,所以声明匿名类型变量要使用var

使用var来声明隐式类型。但对于数据类型并非匿名类型的情况下,建议使用显示数据类型。

var anonymous1 = new { Field1 = "sss", Field2 = "bbb" };

var anonymous2 = new { Field1 = "sss", Field2 = "CCC" };

var anonymous3 = new { Field1 = "ttt" };

var iy = "string";

无法将anonymous1anonymous3互相赋值,无法将nim iy互相赋值,但anonymous1anonymous2可以互相赋值

 

1.5硬编码造成的精度丢失问题

 

如果输入的数字字面值是含有小数,那么计算时默认为double类型,不含有小数,则认为是int类型;以f,d,m结尾的数被认为是floatdoubledecimal。所以涉及的相关运算时,注意写法。例如:

 

//运算结果为1.0

 

float res = 3 / 2;

 

//运算结果为1.5

 

float ress = 3f / 2f;

 

//这是错误写法,因为2.2这种写法是一个double类型

 

float f = 2.2;

 

//最后一个字符为f或F则表示float类型

 

float ff = 2.2f;

 

//这是错误写法,因为2.2是double类型,所以运算结果为double类型,无法double类型隐式转换float类型

 

//强制转换可以,但是可能会造成精度丢失。

 

float fff = 1 / 2.2;

2 控制流语句

2.1 foreach语句

使用foreach语句操作集合,禁止循环操作过程中修改集合中的元素。

 

try
{
  Liststring> list = new Liststring> { "first", "second", "administrator", "letter",         "join" };
  foreach (var item in list)
  {
    if (item.Contains("l")) list.Remove(item);
  }
}
catch (Exception ex)
{
  Console.WriteLine(ex.Message);
}

 

异常信息:集合已修改;可能无法执行枚举操作。

异常类型:System.InvalidOperationException

2.2 if-else语句

if(condition1){}

else if(condition2){}

else if(condition3){}

else{}

与之等价的写法更好理解。

if(condition1)

else

{

  if(condtion2)

  else

  {

    if(condition3){}

    else{}

  }

}

2.3 for循环

一般循环的循环变量为int型,但是其他类型如floatdouble等也是可以使用的。

2.4 Switch语句

常常将Switch用作单一匹配,但不要忘记其多匹配功能,如下面的代码:

 

string sign ="b";
switch (sign)
{
     case "a":
     case "b":
           Console.WriteLine("多匹配方式");
         break;
     case "z":
         break;
     default:
         break;
}

 

还可以向下面这样,使用Switchreturn组合。

 

 

 

    private int Switch()
     {
            int i = 0;
            switch (i)
            {
                case 0:
                    {
                        return 0;
                    }
                case 1:
                    {
                        return 1;
                    }
                default:
                    {
                        return 2;
                    }
            }
      }

3

3.1 配置文件中使用静态字段或静态属性

静态变量是在静态变量所属类初次使用时被初始化的,当静态字段被初始化后,之后每次调用获得的值都是初始化时赋给静态字段的值,除非在这个过程中显示地给静态字段赋值。而静态属性的某些行为类似于静态方法。如下例:

 

    public class Sys
    {
        /// 
        /// 执行时间
        /// 
        public static string ExecuteTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

        public static string ExecuteDate
        {
            get
            {
                return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            }
            set { }
        }
    }
     static void Main(string[] args)
        {
            Console.WriteLine("字段:"+Sys.ExecuteTime);

            Console.WriteLine("属性:"+Sys.ExecuteDate);

            Thread.Sleep(2000);

            Console.WriteLine("字段0:" + Sys.ExecuteTime);

            Console.WriteLine("属性0:" + Sys.ExecuteDate);

            Thread.Sleep(2000);
            Sys.ExecuteDate = DateTime.Now.ToString("yyyy");
            Console.WriteLine("字段1:" + Sys.ExecuteTime);

            Console.WriteLine("属性1:" + Sys.ExecuteDate);
            Console.Read();

        }

 

运行上述代码,输出结果如下:

技术分享图片

由输出结果可以得出:上面的用法中,静态字段每次调用获得的值都是同一个,即初始化时所赋的值;而调用静态属性每次获得的值都是不同的,每次调用都执行一次get方法。

3.2 虚方法中不要含有业务逻辑

使用virtual修饰符修饰类的方法,那么这个方法就可以在派生类中重写,如果原来的方法包含业务逻辑,派生类重写这个方法后,由于派生类将父类中的虚方法完全覆盖,导致虚方法中的业务逻辑永远不会被执行。

3.3 指定构造器

为了避免因缺少可供访问的默认构造器而造成错误,要在派生类构造器的头部显示指定要运行哪一个基类构造器。

3.4 对比virtualabstract

为支持重写,基类中必须为要在子类中重写的成员之前添加virtual修饰符,子类成员要标记为override

使用abstract定义抽象方法。抽象方法没有具体实现,必须在子类方法中实现抽象方法。

虚方法是可以有具体实现的,不过具体实现会在子类的重载中被覆盖。

3.5默认初始值

字段或属性默认初始值随类型的不同而不同。

bool默认初始值为false,对象类型默认初始值为nullint类型为0floatdouble0.0char\0

3.6 readonly

readonly只能用于字段,它指明被其修饰的字段只能在构造函数中修改,或在声明时指定。但对于数组来说情况稍有不同,使用此修饰符修饰数组,那么不允许使用new运算符创建同一个数组的新实例,但可以修改数组中的元素,即使这样的操作不是在构造函数中进行的。

3.7 构造函数的调用问题

如果实例化一个子类,那么子类的构造函数及其父类的构造函数的调用过程是怎么样的呢?调用具有怎样的层次关系?如果父类有几个重载的构造函数而子类未指定构造函数,那么将调用父类的哪一个构造函数呢?

对于继承中涉及到的构造函数的调用问题是比较复杂的。

首先,父类的构造函数先于子类的构造函数被调用。

其次,如果父类和子类都没有自定义构造函数,那么调用的都是默认的构造函数。

 

    public class Child : Father
    {
        public Child()
        {
            Console.WriteLine("Call Child()");
        }
    }

    public class Father
    {
        public Father()
        {
            Console.WriteLine("Call Father()");
        }

        public Father(string ss)
        {
            Console.WriteLine("Call Father(String)"+ss);
        }
   }

 

//实例化

Child c = new Child();

输出:

技术分享图片

若在Child 指定调用父类的构造函数:

  public Child():base("f")

        {

            Console.WriteLine("Call Child()");

        }

输出:

技术分享图片

结论:从中可看出父类构造函数先于子类构造函数被调用,若子类构造函数不指定调用哪一个,一般会根据参数自动匹配。

 

3.8 静态类和静态成员

 

1)静态类是密封的,因此不可被继承。

 

2)静态类不能包含实例构造函数,但仍可声明静态构造函数以分配初始值或设置某个静态状态

 

3)静态方法和属性只能访问静态字段和静态事件

 

4)静态成员在第一次被访问之前并且在任何静态构造函数(如调用的话)之前初始化。

 

5)静态构造函数有以下特点:

 

静态构造函数既没有访问修饰符,也没有参数。

 

在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化

 

无法直接调用静态构造函数。

 

在程序中,用户无法控制何时执行静态构造函数。

 

静态构造函数的典型用途是:当类使用日志文件时,将使用这种构造函数向日志文件中写入项。

 

静态构造函数在为非托管代码创建包装类时也很有用,此时该构造函数可以调用 LoadLibrary 方法。

 

3.9 可访问性

 

非嵌套类型:不嵌套在其他类型中的顶级类型的可访问性只能是 internal 或 public。 这些类型的默认可访问性是 internal。

 

嵌套类型:嵌套类型的可访问性取决于它的可访问域,该域是由已声明的成员可访问性和直接包含类型的可访问域这二者共同确定的。 但是,嵌套类型的可访问域不能超出包含类型的可访问域。

 

属于

默认的成员可访问性

该成员允许的声明的可访问性

enum

public

class

private

public

protected

internal

private

protected internal

interface

public

struct

private

public

internal

private

 

 

 

 

 

 

 

 

注:

1)访问修饰符internal只有在同一程序集的文件中,内部类型或成员才是可访问的 

访问修饰符protected internal访问仅限于从包含类派生的当前程序集或类型

3.10尽量使用属性而不是字段

可以对属性进行更灵活的控制,所以应尽量使用属性,例如下面的代码:

 

     //只有在类内部可以设置属性值
        public string FileName { private set; get; }

        //可以将计算步骤放在get中
        public int FileSize 
        {
            get 
            {
                int size = 0;
                return size;
            }
        }

 

3.11 接口中的属性

在接口中定义属性,若只包含Get块,那么接口的实现类中也只能包含Get块。

例如:

 

  public class Child : Face
    {
        public string Field
        {
            get { throw new NotImplementedException(); }
        }
    }
    public interface Face
    {
        string Field
        {
            get;
        }
  }

 

4 泛型

4.1string.Join方法不能识别泛型

string JoinStr(T set)

{

  Return string.Join(“,”,set);

}

上面这个方法不能正确返回拼接后的字符串,正确的方式如下:

string JoinStrRight(IEnumerable  set)

{

  Return string.Join(“,”,set);

}

 

5 集合

5.1 关于集合的标准查询运算符

1)First FirstOrDefaultSingleSingleOrDefault

First:查找第一个符合条件的元素,如果没有找到,抛异常。

FirstOrDefault:查找第一个符合条件的元素,如果没有找到,返回null

Single:找到符合条件的一个元素,如果找不到,抛异常;如果有多个元素符合条件,抛异常。

SingleOrDefault:找到符合条件的一个元素,如果找不到,返回null;如果有多个元素符合条件,抛异常。

结论:

如果集合中可能只有一个或没有符合条件的元素,用FirstOrDefaultSingleOrDefault都可,不建议用FirstSingle,因为要自己处理异常。

如果集合中可能有多个或没有符合条件的元素,建议使用FirstOrDefault

2)Select

Select为将集合中的元素“映射”为其他形式,而不是筛选出符合某一条件的元素。

3)Except

假设有两个类型为List集合,list1list2

list1.Except(list2);返回结果为从list1中去掉list1list2相同的元素后剩余的部分;

list2.Except(list1);返回结果为从list2中去掉list1list2相同的元素后剩余的部分;

若要求两个集合相同的部分,方法为:

List list3 = list1.Except(list2);

List same = list1.Except(list3);

若要求两个集合的并集且无重复,方法为:

List listA = list1.Except(list2);

List listB = list2.Except(list1);

List all = listA.AddRange(listB);

5.2 提高集合插入性能

对于List

如果集合大小已经小于集合默认容量,此方法为复杂度为 o (1) 操作。如果需要增加以容纳新元素的容量,此方法将变为 O (n) 操作,其中 n 是数组大小。

对于LinkedList

LinkedList.AddFirst()AddLast()的运算复杂度为 O(1)

对于StackQueue

如果集合大小已经小于集合默认容量,此方法为复杂度为 o (1) 操作。如果需要增加以容纳新元素的容量,此方法将变为 O (n) 操作,其中 n 是数组大小。

结论:如果能事先确定大小,则确定大小。

6 异常处理

6.1 优先考虑在最外层捕获异常

假设FunctionAFunctionBFunctionC三个方法,FunctionA调用了FunctionBFunctionB调用了FunctionC,那么首先考虑对FunctionA使用try...catch语句。

6.2 try...finallyreturn

finally块中的语句总会执行,除了finally语句块中的语句抛异常以外。

 

       try 
            {
                Console.WriteLine("Execute try");
                return;
            }
            catch {
                return;
            }
            finally
            {
                Console.WriteLine("Execute finally");
            }

 

输出:

技术分享图片

7 扩展

7.1System.Linq的行为进行扩展

扩展方法:

 

public static class Extend
{
        public static string ToString(this string input, int str)
        {
            return "输入的是:" + str;
        }
}

 

Main方法中调用:

using System;

namespace ConsoleApp_CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            string str = "输入";
            Console.WriteLine(str.ToString(1));
            Console.Read();
        }
        
    }
}

编译Main方法,无法编译通过,错误信息如下:

技术分享图片

原因是:未引入扩展方法所在命名空间。在调用扩展方法时,引入扩展方法的命名空间即可,即使用如上例中使用将using ConsoleApp_CSharp.Extend;语句引入。

 

C#要点

标签:upper   instr   switch   增加   protected   结果   控制   托管代码   abc   

原文地址:http://www.cnblogs.com/hdwgxz/p/7954951.html

上一篇:C# linq to xml 简单示例

下一篇:win10安装


评论


亲,登录后才可以留言!