改善C#程序的方法 c# is和as的区别 c# is和as的区别

写在开头: html

http://www.cnblogs.com/luminji    157个建议_勘误表c++

一:属性算法

属性和方法同样。也能够是virtual和abstract.数据库

条款2:运行时常量(readonly)优于编译时常量(const)

运行时常量优于编译时常量【能正确运行才是关键】。编译时常量比运行时常量稍微块一点,可是缺少灵活性。性能很是关键,其值永远不变的状况下,咱们才应该使用编译时常量。编程

c# readonly 运行时常量【构造器一旦执行则不能对值进行修改】    const编译时常量c#

编译时常量编译后会把该常量替换成常量的值,相似于c++的宏【编译时常量只能够用于基元类型(整数浮点数枚举字符串)】。运行时编译后任然是该变量的引用windows

条款3:操做符is或as优于强制转型

操做符as和is都只检查被转换对象的运行时类型,并不执行其余的操做。若是被转换对象的运行时类型既不是所转换的目标类型,也不是其派生类型,那么转型将告失败跨域

as操做符不能用于值类型,int值类型,不能为null.数组

as是直接转安全

只有当咱们不能使用as操做符来进行类型转换时,才应该使用is操做符。

若是咱们打算使用as来作转型,那么再使用is检查就没有必要了。直接将as操做符的运算结果和null进行比对就能够了,这样比较简单

 

c# is和as的区别
is就是处于对类型的判断。返回true和false。若是一个对象是某个类型或是其父类型的话就返回为true,不然的话就会返回为false。另外is操做符永远不会抛出异常。代码以下:

​System.Boolean b1 = (o is System.Object);//b1 为true
​System.Boolean b2 = (o is Employee);//b2为false

若是对象引用为null,那么is操做符老是返回为false,由于没有对象能够检查其类型,就像下面代码同样

if(o is Employee) 

{
   Employee e = (Employee) o;
   //在if语句中使用e
}

as :as必须和能够为NUll类型使用。转int则不行 

Employee e = o as Employee;
if(e != null)
{
   //在if语句中使用e
}

这种as操做即使等同于上面代码,同时只进行了1次的类型检查,因此提升了性能。若是类型相同就返回一个非空的引用,不然就返回一个空引用。

 

 

 

条款4:使用Conditional特性代替#if条件编译

条款5:老是提供ToString()方法

条款6:明辨值类型和引用类型的使用场合

    值类型用于存储数据,引用类型用于定义行为

条款7:将值类型尽量实现为具备常量性和原子性的类型

条款8:确保0为值类型的有效状态

枚举将None=0声明出来

条款9:理解几个相等判断之间的关系

ReferenceEquals(): 无论比较的是引用类型仍是值类型,该方法都判断的是“引用相等”,而非“值相等”,意味着若是咱们使用此来比较两个值类型,其结果永远返回false。即便咱们将一个值类型和自身进行比较,ReferenceEquals()的返回值还是false。致使这种结果的缘由在于装箱

Object.Equals() 默认是引用判断,可是值类型例外,判断值类型时须要重写Equals()方法。若是两个值类型变量的类型相同,而且内容一致,这两个变量才被认为相等。

判断是否引用的同一个对象时的注意点:    string a="aa";        string b = "aa"; 两个比较都是相等的。这是由于系统并无给字符串b分配内存,只是将"aa"指向了b。因此a和b指向的是同一个字符串(字符串在这种赋值的状况下作了内存的优化)
      

//静态Object.Equals()方法的实现
public static bool Equals( object left, object right )
{
  // 检查是否引用相等。
  if (left == right ) //System.object类型的==实现的是引用相等
    return true;
  // 二者同时为null引用的状况在上面已经处理。
  if ((left == null) || (right == null))
    return false;
  return left.Equals (right);
}

相等的数学属性:自反(任何对象都与其自身相等)、对称(相等判断的顺序是可有可无的)和可传递(a=b b=c 则a=c)

条款10:理解GetHashCode()方法的缺陷

注意; a="aa" b="aa"  unity中能够经过散列码GetHashCode()间接的查看两个变量的地址是否相等,可是数组的地址是连续存储的,可是输出的散列码确实同样的???。

条款11:优先采用foreach循环语句

int [] foo = new int[100]; 
// 循环1:
foreach ( int i in foo)
  Console.WriteLine( i.ToString( ));
// 循环2:
for ( int index = 0;  index < foo.Length;  index++ )
  Console.WriteLine( foo[index].ToString( ));
// 循环3:
int len = foo.Length;
for ( int index = 0;  index < len;  index++ )
  Console.WriteLine( foo[index].ToString( ));

c#1.0以上则第一个最好(c#1.0的化 第二个最好【由于第一个由装箱】)。

在1.0版本的编译器产生的代码中,在数组上使用foreach语句其实是经过IEnumerator接口来遍历数组,而这会致使装箱与拆箱操做:遍历类型=(遍历类型)Current(接口类型);  1.0之后用的是for来遍历的

  foreach语法简洁   自带finally{ dispose()  }  释放内存 。

扩展: Unity5.5版本以后修复了foreach的GC  http://www.mamicode.com/info-detail-2103245.html

第三个最差:

缘由解析:安全托管环境中每一个内存都会检查,而经过将Length变量放到循环以外,实际上阻碍了JIT编译器移除循环中的范围检查。

反编译后: 

int len = foo.Length;
for ( int index = 0;  index < len;  index++ )
{
  if ( index < foo.Length )
    Console.WriteLine( foo[index].ToString( ));
  else
    throw new IndexOutOfRangeException( );
}

CLR 会在访问每个特定数组元素以前,产生一个数组界限(并不是上面的len变量)测试。C#编译器和JIT编译器能够确保循环中的数组界限是安全的。只要循环变量不是数组的Length属性,每一次迭代时都会执行数组界限检查。破坏了JIT自己的优化

 

 

《编写高质量代码改善C#程序的157个建议》

string:

1.  值类型转string时,须要重写ToString(),使其调用值类型中的ToString()方法。由于值类型中的ToString()时非托管代码,能够直接操做内存来完成操做,效率高不少。

Debug.LogError("wyj "+9);                     //须要装箱
        Debug.LogError("wyj " + 9.ToString());  //效率高

2.  字符串拼接时,使用StringBuilder。若是没有先定义长度的话,则默认分配长度未16。当字符串小于16时,不会重写分配。32>=str>=16时,则重写分配,使之成为16的倍数。注意指定的长度摇合适,过小须要频繁分配内存。

3.  Format格式化,内部是使用的stringbuilder.

枚举:最好不要赋值  , 若是赋值的话最好从0开始

4.重载运算符:

用户本身定义的运算方式,通常用于对几个对象之间内部进行的一些操做。

5.  重写Equals时也要重写GetHashCode

  若是自定义对象被用做基于散列集合的键,则建议重写Equals方法。查询时是基于key值的HashCode来查找键值的。【若是须要全部new的对象当成一个key,即须要重写HashCode(),包装一个int值的HashCode来看成该对象建立的全部对象的HashCode();

  字符串不一样到那时产生的HashCode()是同样的状况和缘由。获得的哈希值是int型,而若是是字符串,字符串的长度和这个值的大小是正比,过长的字符串会致使这个值超过int.max,因此会哈希值同样的状况,解决方案是在这个哈希值的前边把方法名加上。

string str1 = "ABCDEa123abc";
        string str2 = "ABCDFB123abc";
        Debug.Log(str1.GetHashCode()+"          "+ str2.GetHashCode());
    }
    public int hashCode()
    {
        int h = hash;  // hash默认值为0
        int len = count;// count是字符串的长度
        if (h == 0 && len > 0)
        {
            int off = offset;  // offset 是做为String操做时做为下标使用的
            char val[] = value;// value 是字符串分割成一个字符数组
            for (int i = 0; i < len; i++)
            {
                // 这里是Hash算法体现处, 能够看到H是一个哈希值,每次是将上次一算出的hash值乘以31 而后再加上当前字符编码值,
                //因为这里使用的是int确定会有一个上限,当字符长时产生的数值过大int放不下时会进行截取,一旦截取HashCode的正确性就没法保证了,
                //因此这点能够推断出HashCode存在不相同字符拥有相同HashCode。
                h = 31 * h + val[off++];  
            }
            hash = h;
        }
        return h;
    }

14. 正确实现浅拷贝和深拷贝

 浅拷贝: 值类型拷贝的是值  引用类型拷贝的是引用

 深拷贝:  值类型拷贝的是值  引用类型拷贝的是引用指向的值

第2章 集合和LINQ

16. 元素数量可变的状况下不该该使用数组

不要让数组成为大对象【>85000字节数】,大对象的回收效率低

17.多数状况下使用foreach遍历

  理由:语法简洁   自带finally{ dispose()  }  释放内存 。

   for[索引器实现的]   foreach(迭代器实现)  

 foreach不应修改内部元素的缘由:   foreach对集合版本进行判断,任何对集合的增删改查都会使版本号+1 . MoveNext() 会进行版本号的检查,有变更时会抛出异常【System.InvalidOperationException】。

foreach (int value in list)
            {
                Console.WriteLine("值: " + value);
                list.Remove(value);
            }

 

通常使用匿名函数或者lambda 来对数据进行查询

第三章 泛型、委托和事件

32 老是优先使用泛型

若T指向的数据类型是一致的,那么泛型对象间能够共享静态成员。可是为了规避混淆,泛型中要避免申明静态成员。

private void Start()
    {
        A_Books<int> aint = new global::A_Books<int>();
        A_Books<string> astr = new global::A_Books<string>();

        Debug.Log(A_Books<int>.num+"          " + A_Books<string>.num);  //6 6

        A_Books<int> aint_1 = new global::A_Books<int>();
        Debug.Log(A_Books<int>.num); //7
    }
}

public class A_Books<T>
{
    public static int num=5;
    public A_Books()
    {
        num++;
    }
}

泛型方法: 非泛型类型中的泛型方法,并不会在运行时的本地代码中生成不一样的类型。

泛型参数增长该泛型参数的行为。编码时多考虑对泛型进行约束

 

 

使用default为泛型类型变量指定初始值:

当返回值是一个泛型类型时,则

37 . 使用lambda表达式代替匿名方法:

38. 当心闭包中的陷阱。

闭包: 指可以读取其余函数内部变量的函数。

 所谓闭包对象:若是匿名方法(Lambda)引用了某个局部变量(在for中),编辑器就会自动将该对象引用提高到闭包对象中。

 这样即便代码执行后离开了局部变量i的做用域【如for循环】,包含该闭包对象的做用于也还存在。

上述代码避免闭包:

闭包的实现过程: 经过捕获变量来实现的闭包。

List<MethodInvoke> list = new List<MethodInvoke> ();
	void TestFun()
	{
		for(int row=0;row<5;row++)
		{
			int count=row*10;
			list.Add(delegate
				{
					print(count);
					count=count+1;
				});
		}

		//每一个元素都是一个委托,每一个委托都是建立了一个新的count
                //若是把count换成是row,捕获变量从5开始,猜想这个委托类的建立时在调用时才生成的
		foreach(MethodInvoke t in list)
		{
			t();
		}

		print("调用同一个委托");
		//此时count是上次foreach中 list[0]委托中已经建立过委托实例了,即该实例类中也有了count变量,
		//因此再次调用是上次调用后获得的值=1。

		list[0]();//1
		list[0]();//2
		list[0]();//3

		print("调用另外一个委托");
		list[1]();//11
	}

40. 泛型参数兼容泛型接口的不可变型        泛型的可变性// 基础不够之后再研究 ???

协变: 让返回值类型返回比声明的类型派生程度【子类比父类派生程度大】更大的类型,就是协变。

逆变:方法的参数能够是委托或者泛型接口的参数类型的基类。

out在c#4.0 新增功能,能够在泛型接口和委托中使用,用来让类型支持协变。

除非考虑到该委托声明确定不会用于可变性,不然为委托中的泛型参数指定out关键字将会扩展该委托的应用。

public delegate TResult Func<out TResult>();

 

第四章  资源管理

托管资源: 由CLR管理和分配

非托管:不受CLR  管理的对象,套接字,文件,数据库连接,windows内核,com对象

 

53.必要时应将再也不使用的对象的引用赋值为null

 引用赋值为null 没 必要的状况: 

      局部变量和方法的参数变量,不管咱们是否在方法内部将局部变量赋值为null,a=null该语句会被忽略。这也说明JIT编译器是一个优化过的编译器。若是是Release模式,则a=null都不会编译进运行时。

 引用赋值为null必要的状况

     静态字段,好比建立一个对象,该对象中有静态字段,当该局部变量对象被释放后,该对象中的静态字段不会被释放。由于静态字段建立后,该“根”就一直存在。因此手动置为null.  这也是最好少用静态字段的缘由。

 

54. 为无用字段标注不可序列化

55.利用定制特性减小可序列化的字段

 

第6章 异步 多线程 任务 并行

71.  区分异步和多线程应用场景

DMA(Direct Memory Access): 直接内存访问,是一种不通过CPU而直接进行内存数据存储的数据交换模式。几乎不损耗CPU. CLR异步编程模型就是充分利用DMA功能释放CPU压力。 

多线程本质: 建立一个线程,一直等待获取数据,一直占着CPU资源。线程不是一个计算机硬件的功能,而是操做系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,因此线程须要操做系统投入CPU资源来运行和调度。

异步本质:开始异步操做时,CLR把工做交给线程池中的某个线程进行完成。当开始IO操做时,异步会把工做线程还给线程池。至关于获取工做不会再占用CPU资源,直到异步完成,获取数据结束后,异步才会通知回调的方式通知线程池。先干别的事,当它须要的数据准备完毕后,再会来干这件事。

计算 密集型工做: 多线程,(例如耗时较长的图形处理和算法执行)

IO 密集型工做: 采用异步机制。(文件,网络数据修改,数据库操做、Web Service、HttpRequest以及.Net Remoting等跨进程的调用)

 

 

多线程建立线程,一直等待,获取数据,获取完毕。异步线程池中的线程,等待。开始IO操做时,还给线程池,获取完毕后回调。

异步,让线程池中的一个线程获取网页,获取后开始IO操做(读取网页),此时把线程还给线程池,直到异步完成,即获取网页完毕后,异步才会经过回调的方式通知线程池。

72. 在线程同步中使用的信号量

EventWaitHandle 维护一个内核产生的布尔类型对象(“阻滞状态”),若是值=false,那么在上边等待的线程就阻塞【应用程序域内的线程同步】

Semaphore: 维护一个内核产生的整形变量。值=0,则在上边等待的线程就阻塞。>0解除阻塞,每解除一个其值减1.【应用程序域内的线程同步】

Mutex : 能够跨域阻塞和解除阻塞。

lock锁注意点:

1. 主要是锁对象,不能锁值类型【值拷贝方式】  ,

2. 不能锁字符串,没有必要并且很危险【若是两个变量分配了相同内容的字符串,那么两个引用指的同一个内存,用了锁后,实际锁的时同一个对象,会致使程序崩溃】

3. 不能写成lock(this)   会new几个对象,达不到锁定的目的。

同步锁时很耗费时的。线程池中的线程默认是后台线程。  建立的线程默认是前台线程【默认isbackground=false 前台线程不退出,应用程序的进程则一直存在,要杀死】

75.  线程并非实时当即启动

76.警戒线程的优先级

77. 正确中止线程

问题: 和启动线程同样,不是想停就马上停的。得干完手头要紧的活,好比如今在执行非托管代码,引起异常得回到托管代码中。

线程中止主要取决于工做线程是否能主动访问调用者的中止请求。

标准的取消模式:协议式取消。

CancellationTokenSource cts=new CancellationTokenSource();

cts.Token.Register(fun()); //线程中止时的回调 

cts.Cancel();  //发送Cancel信号   线程中止

socket  1000台客户端异步技术 只需几个线程就能够了(取决于心跳频率)

 

79. 使用TreadPool或BackgroundWorker代替Thread

80. Task代替ThreadPool

ThreadPool: 不支持线程的取消,完成,失败通知。不支持线程执行的前后顺序。

81. Parallel简化同步状态

82. 并行

第二部分 架构篇

第7章  成员设计

90.

<1 . 不要为抽象类提供公开的构造方法,抽象类设计只是为了继承,而不是用于生成实例对象

 <2. 可见字段应该重构为属性,属性和字段的区别:一个是方法,一个是字段

 <3. 谨慎把数组或者集合做为属性

 <4. 构造方法应初始化主要属性和字段。【一个猫生下来就已经具有尾巴了】

 <5. 区别对待override和new.[new 重写覆盖了父类方法,至关于该类中的一个新方法,和父类中的方法没有一点关系]

 <6. 避免在构造方法中调用虚方法

 <7. 成员优先考虑公开基类型或者接口

 <8 . 用params减小重复参数

 <9. 重写时不该该使用子类参数

建议100: 静态方法和实例方法没有区别

101. 使用扩展方法,向现有类型“添加”方法

 

第八章 类型设计

 

103. 区分组合和继承的应用场合

组合; 在新类A中声明 类B,C,D的实例。【有一个的概念】

107. 区分静态类和单例

静态类不是一个真正的对象,可是单例类时一个对象。

109. 谨慎使用嵌套类

当某一个类须要访问另外一个类型的私有成员时,才实现为嵌套类

111. 避免双重耦合【常见的解耦就是提炼接口】

112. 把现实世界中的对象抽象为类【猫,狗】,将可复用对象圈起来就是命名空间【植物,动物】

 

第9章 安全性设计

考虑可能出现的最大值:定义加工资,最大值。checked{} 关键字i行核实,会主动抛出异常

114. MD5 再也不安全 【穷举法破解】

115. HASH 检验文件是否被纂改

116. 避免非对称算法加密

117. ......

 

编码规范和习惯:

 <1.  Company.Component 命名空间命名

 <2. 考虑命名空间使用复数,System.Books   不要System.AllBook

 <3. 用名词和名词组给类型命名    推荐ScoreManager    不要SoreManage

 <4. 考虑让派生类的名字以基类名字做为后缀

 <5. 泛型类型参数要以T做为前缀

 <6. 以复数命名枚举类型,以单数命名枚举元素【Week  不要Day】

 <7. 用camelCasing命名私有字段和局部变量

 <8. 常量如下划线的方式   TASK_STATE_CANCELED    s_ 静态变量   

 <9. 考虑使用确定性的短语命名bool属性  IsEnabled 

 <10.优先使用后缀做为一个类型的信版本,不到不得已并不推荐  Book1   Book2

 <11. 委托和事件加上上级后缀 HttpDelegate()

 <12. 事件处理器函数采用组合式命名: Button_SizeChanged()

 

代码整洁: 

代码整洁的要求之一,就是尽可能减小代码。如省略默认的访问修饰符

<1. 使用表驱动法避免过长的if和switch分支

 

<2. 使用匿名方法,Lambda表达式代替方法   若是方法体小于3行

<3. 使用事件访问器替换公开的事件成员变量

    

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

写在开头: 

http://www.cnblogs.com/luminji    157个建议_勘误表

一:属性

属性和方法同样。也能够是virtual和abstract.

条款2:运行时常量(readonly)优于编译时常量(const)

运行时常量优于编译时常量【能正确运行才是关键】。编译时常量比运行时常量稍微块一点,可是缺少灵活性。性能很是关键,其值永远不变的状况下,咱们才应该使用编译时常量。

c# readonly 运行时常量【构造器一旦执行则不能对值进行修改】    const编译时常量

编译时常量编译后会把该常量替换成常量的值,相似于c++的宏【编译时常量只能够用于基元类型(整数浮点数枚举字符串)】。运行时编译后任然是该变量的引用

条款3:操做符is或as优于强制转型

操做符as和is都只检查被转换对象的运行时类型,并不执行其余的操做。若是被转换对象的运行时类型既不是所转换的目标类型,也不是其派生类型,那么转型将告失败

as操做符不能用于值类型,int值类型,不能为null.

as是直接转

只有当咱们不能使用as操做符来进行类型转换时,才应该使用is操做符。

若是咱们打算使用as来作转型,那么再使用is检查就没有必要了。直接将as操做符的运算结果和null进行比对就能够了,这样比较简单

 

c# is和as的区别
is就是处于对类型的判断。返回true和false。若是一个对象是某个类型或是其父类型的话就返回为true,不然的话就会返回为false。另外is操做符永远不会抛出异常。代码以下:

​System.Boolean b1 = (o is System.Object);//b1 为true
​System.Boolean b2 = (o is Employee);//b2为false

若是对象引用为null,那么is操做符老是返回为false,由于没有对象能够检查其类型,就像下面代码同样

if(o is Employee) 

{
   Employee e = (Employee) o;
   //在if语句中使用e
}

as :as必须和能够为NUll类型使用。转int则不行 

Employee e = o as Employee;
if(e != null)
{
   //在if语句中使用e
}

这种as操做即使等同于上面代码,同时只进行了1次的类型检查,因此提升了性能。若是类型相同就返回一个非空的引用,不然就返回一个空引用。

 

 

 

条款4:使用Conditional特性代替#if条件编译

条款5:老是提供ToString()方法

条款6:明辨值类型和引用类型的使用场合

    值类型用于存储数据,引用类型用于定义行为

条款7:将值类型尽量实现为具备常量性和原子性的类型

条款8:确保0为值类型的有效状态

枚举将None=0声明出来

条款9:理解几个相等判断之间的关系

ReferenceEquals(): 无论比较的是引用类型仍是值类型,该方法都判断的是“引用相等”,而非“值相等”,意味着若是咱们使用此来比较两个值类型,其结果永远返回false。即便咱们将一个值类型和自身进行比较,ReferenceEquals()的返回值还是false。致使这种结果的缘由在于装箱

Object.Equals() 默认是引用判断,可是值类型例外,判断值类型时须要重写Equals()方法。若是两个值类型变量的类型相同,而且内容一致,这两个变量才被认为相等。

判断是否引用的同一个对象时的注意点:    string a="aa";        string b = "aa"; 两个比较都是相等的。这是由于系统并无给字符串b分配内存,只是将"aa"指向了b。因此a和b指向的是同一个字符串(字符串在这种赋值的状况下作了内存的优化)
      

//静态Object.Equals()方法的实现
public static bool Equals( object left, object right )
{
  // 检查是否引用相等。
  if (left == right ) //System.object类型的==实现的是引用相等
    return true;
  // 二者同时为null引用的状况在上面已经处理。
  if ((left == null) || (right == null))
    return false;
  return left.Equals (right);
}

相等的数学属性:自反(任何对象都与其自身相等)、对称(相等判断的顺序是可有可无的)和可传递(a=b b=c 则a=c)

条款10:理解GetHashCode()方法的缺陷

注意; a="aa" b="aa"  unity中能够经过散列码GetHashCode()间接的查看两个变量的地址是否相等,可是数组的地址是连续存储的,可是输出的散列码确实同样的???。

条款11:优先采用foreach循环语句

int [] foo = new int[100]; 
// 循环1:
foreach ( int i in foo)
  Console.WriteLine( i.ToString( ));
// 循环2:
for ( int index = 0;  index < foo.Length;  index++ )
  Console.WriteLine( foo[index].ToString( ));
// 循环3:
int len = foo.Length;
for ( int index = 0;  index < len;  index++ )
  Console.WriteLine( foo[index].ToString( ));

c#1.0以上则第一个最好(c#1.0的化 第二个最好【由于第一个由装箱】)。

在1.0版本的编译器产生的代码中,在数组上使用foreach语句其实是经过IEnumerator接口来遍历数组,而这会致使装箱与拆箱操做:遍历类型=(遍历类型)Current(接口类型);  1.0之后用的是for来遍历的

  foreach语法简洁   自带finally{ dispose()  }  释放内存 。

扩展: Unity5.5版本以后修复了foreach的GC  http://www.mamicode.com/info-detail-2103245.html

第三个最差:

缘由解析:安全托管环境中每一个内存都会检查,而经过将Length变量放到循环以外,实际上阻碍了JIT编译器移除循环中的范围检查。

反编译后: 

int len = foo.Length;
for ( int index = 0;  index < len;  index++ )
{
  if ( index < foo.Length )
    Console.WriteLine( foo[index].ToString( ));
  else
    throw new IndexOutOfRangeException( );
}

CLR 会在访问每个特定数组元素以前,产生一个数组界限(并不是上面的len变量)测试。C#编译器和JIT编译器能够确保循环中的数组界限是安全的。只要循环变量不是数组的Length属性,每一次迭代时都会执行数组界限检查。破坏了JIT自己的优化

 

 

《编写高质量代码改善C#程序的157个建议》

string:

1.  值类型转string时,须要重写ToString(),使其调用值类型中的ToString()方法。由于值类型中的ToString()时非托管代码,能够直接操做内存来完成操做,效率高不少。

Debug.LogError("wyj "+9);                     //须要装箱
        Debug.LogError("wyj " + 9.ToString());  //效率高

2.  字符串拼接时,使用StringBuilder。若是没有先定义长度的话,则默认分配长度未16。当字符串小于16时,不会重写分配。32>=str>=16时,则重写分配,使之成为16的倍数。注意指定的长度摇合适,过小须要频繁分配内存。

3.  Format格式化,内部是使用的stringbuilder.

枚举:最好不要赋值  , 若是赋值的话最好从0开始

4.重载运算符:

用户本身定义的运算方式,通常用于对几个对象之间内部进行的一些操做。

5.  重写Equals时也要重写GetHashCode

  若是自定义对象被用做基于散列集合的键,则建议重写Equals方法。查询时是基于key值的HashCode来查找键值的。【若是须要全部new的对象当成一个key,即须要重写HashCode(),包装一个int值的HashCode来看成该对象建立的全部对象的HashCode();

  字符串不一样到那时产生的HashCode()是同样的状况和缘由。获得的哈希值是int型,而若是是字符串,字符串的长度和这个值的大小是正比,过长的字符串会致使这个值超过int.max,因此会哈希值同样的状况,解决方案是在这个哈希值的前边把方法名加上。

string str1 = "ABCDEa123abc";
        string str2 = "ABCDFB123abc";
        Debug.Log(str1.GetHashCode()+"          "+ str2.GetHashCode());
    }
    public int hashCode()
    {
        int h = hash;  // hash默认值为0
        int len = count;// count是字符串的长度
        if (h == 0 && len > 0)
        {
            int off = offset;  // offset 是做为String操做时做为下标使用的
            char val[] = value;// value 是字符串分割成一个字符数组
            for (int i = 0; i < len; i++)
            {
                // 这里是Hash算法体现处, 能够看到H是一个哈希值,每次是将上次一算出的hash值乘以31 而后再加上当前字符编码值,
                //因为这里使用的是int确定会有一个上限,当字符长时产生的数值过大int放不下时会进行截取,一旦截取HashCode的正确性就没法保证了,
                //因此这点能够推断出HashCode存在不相同字符拥有相同HashCode。
                h = 31 * h + val[off++];  
            }
            hash = h;
        }
        return h;
    }

14. 正确实现浅拷贝和深拷贝

 浅拷贝: 值类型拷贝的是值  引用类型拷贝的是引用

 深拷贝:  值类型拷贝的是值  引用类型拷贝的是引用指向的值

第2章 集合和LINQ

16. 元素数量可变的状况下不该该使用数组

不要让数组成为大对象【>85000字节数】,大对象的回收效率低

17.多数状况下使用foreach遍历

  理由:语法简洁   自带finally{ dispose()  }  释放内存 。

   for[索引器实现的]   foreach(迭代器实现)  

 foreach不应修改内部元素的缘由:   foreach对集合版本进行判断,任何对集合的增删改查都会使版本号+1 . MoveNext() 会进行版本号的检查,有变更时会抛出异常【System.InvalidOperationException】。

foreach (int value in list)
            {
                Console.WriteLine("值: " + value);
                list.Remove(value);
            }

 

通常使用匿名函数或者lambda 来对数据进行查询

第三章 泛型、委托和事件

32 老是优先使用泛型

若T指向的数据类型是一致的,那么泛型对象间能够共享静态成员。可是为了规避混淆,泛型中要避免申明静态成员。

private void Start()
    {
        A_Books<int> aint = new global::A_Books<int>();
        A_Books<string> astr = new global::A_Books<string>();

        Debug.Log(A_Books<int>.num+"          " + A_Books<string>.num);  //6 6

        A_Books<int> aint_1 = new global::A_Books<int>();
        Debug.Log(A_Books<int>.num); //7
    }
}

public class A_Books<T>
{
    public static int num=5;
    public A_Books()
    {
        num++;
    }
}

泛型方法: 非泛型类型中的泛型方法,并不会在运行时的本地代码中生成不一样的类型。

泛型参数增长该泛型参数的行为。编码时多考虑对泛型进行约束

 

 

使用default为泛型类型变量指定初始值:

当返回值是一个泛型类型时,则

37 . 使用lambda表达式代替匿名方法:

38. 当心闭包中的陷阱。

闭包: 指可以读取其余函数内部变量的函数。

 所谓闭包对象:若是匿名方法(Lambda)引用了某个局部变量(在for中),编辑器就会自动将该对象引用提高到闭包对象中。

 这样即便代码执行后离开了局部变量i的做用域【如for循环】,包含该闭包对象的做用于也还存在。

上述代码避免闭包:

闭包的实现过程: 经过捕获变量来实现的闭包。

List<MethodInvoke> list = new List<MethodInvoke> ();
	void TestFun()
	{
		for(int row=0;row<5;row++)
		{
			int count=row*10;
			list.Add(delegate
				{
					print(count);
					count=count+1;
				});
		}

		//每一个元素都是一个委托,每一个委托都是建立了一个新的count
                //若是把count换成是row,捕获变量从5开始,猜想这个委托类的建立时在调用时才生成的
		foreach(MethodInvoke t in list)
		{
			t();
		}

		print("调用同一个委托");
		//此时count是上次foreach中 list[0]委托中已经建立过委托实例了,即该实例类中也有了count变量,
		//因此再次调用是上次调用后获得的值=1。

		list[0]();//1
		list[0]();//2
		list[0]();//3

		print("调用另外一个委托");
		list[1]();//11
	}

40. 泛型参数兼容泛型接口的不可变型        泛型的可变性// 基础不够之后再研究 ???

协变: 让返回值类型返回比声明的类型派生程度【子类比父类派生程度大】更大的类型,就是协变。

逆变:方法的参数能够是委托或者泛型接口的参数类型的基类。

out在c#4.0 新增功能,能够在泛型接口和委托中使用,用来让类型支持协变。

除非考虑到该委托声明确定不会用于可变性,不然为委托中的泛型参数指定out关键字将会扩展该委托的应用。

public delegate TResult Func<out TResult>();

 

第四章  资源管理

托管资源: 由CLR管理和分配

非托管:不受CLR  管理的对象,套接字,文件,数据库连接,windows内核,com对象

 

53.必要时应将再也不使用的对象的引用赋值为null

 引用赋值为null 没 必要的状况: 

      局部变量和方法的参数变量,不管咱们是否在方法内部将局部变量赋值为null,a=null该语句会被忽略。这也说明JIT编译器是一个优化过的编译器。若是是Release模式,则a=null都不会编译进运行时。

 引用赋值为null必要的状况

     静态字段,好比建立一个对象,该对象中有静态字段,当该局部变量对象被释放后,该对象中的静态字段不会被释放。由于静态字段建立后,该“根”就一直存在。因此手动置为null.  这也是最好少用静态字段的缘由。

 

54. 为无用字段标注不可序列化

55.利用定制特性减小可序列化的字段

 

第6章 异步 多线程 任务 并行

71.  区分异步和多线程应用场景

DMA(Direct Memory Access): 直接内存访问,是一种不通过CPU而直接进行内存数据存储的数据交换模式。几乎不损耗CPU. CLR异步编程模型就是充分利用DMA功能释放CPU压力。 

多线程本质: 建立一个线程,一直等待获取数据,一直占着CPU资源。线程不是一个计算机硬件的功能,而是操做系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,因此线程须要操做系统投入CPU资源来运行和调度。

异步本质:开始异步操做时,CLR把工做交给线程池中的某个线程进行完成。当开始IO操做时,异步会把工做线程还给线程池。至关于获取工做不会再占用CPU资源,直到异步完成,获取数据结束后,异步才会通知回调的方式通知线程池。先干别的事,当它须要的数据准备完毕后,再会来干这件事。

计算 密集型工做: 多线程,(例如耗时较长的图形处理和算法执行)

IO 密集型工做: 采用异步机制。(文件,网络数据修改,数据库操做、Web Service、HttpRequest以及.Net Remoting等跨进程的调用)

 

 

多线程建立线程,一直等待,获取数据,获取完毕。异步线程池中的线程,等待。开始IO操做时,还给线程池,获取完毕后回调。

异步,让线程池中的一个线程获取网页,获取后开始IO操做(读取网页),此时把线程还给线程池,直到异步完成,即获取网页完毕后,异步才会经过回调的方式通知线程池。

72. 在线程同步中使用的信号量

EventWaitHandle 维护一个内核产生的布尔类型对象(“阻滞状态”),若是值=false,那么在上边等待的线程就阻塞【应用程序域内的线程同步】

Semaphore: 维护一个内核产生的整形变量。值=0,则在上边等待的线程就阻塞。>0解除阻塞,每解除一个其值减1.【应用程序域内的线程同步】

Mutex : 能够跨域阻塞和解除阻塞。

lock锁注意点:

1. 主要是锁对象,不能锁值类型【值拷贝方式】  ,

2. 不能锁字符串,没有必要并且很危险【若是两个变量分配了相同内容的字符串,那么两个引用指的同一个内存,用了锁后,实际锁的时同一个对象,会致使程序崩溃】

3. 不能写成lock(this)   会new几个对象,达不到锁定的目的。

同步锁时很耗费时的。线程池中的线程默认是后台线程。  建立的线程默认是前台线程【默认isbackground=false 前台线程不退出,应用程序的进程则一直存在,要杀死】

75.  线程并非实时当即启动

76.警戒线程的优先级

77. 正确中止线程

问题: 和启动线程同样,不是想停就马上停的。得干完手头要紧的活,好比如今在执行非托管代码,引起异常得回到托管代码中。

线程中止主要取决于工做线程是否能主动访问调用者的中止请求。

标准的取消模式:协议式取消。

CancellationTokenSource cts=new CancellationTokenSource();

cts.Token.Register(fun()); //线程中止时的回调 

cts.Cancel();  //发送Cancel信号   线程中止

socket  1000台客户端异步技术 只需几个线程就能够了(取决于心跳频率)

 

79. 使用TreadPool或BackgroundWorker代替Thread

80. Task代替ThreadPool

ThreadPool: 不支持线程的取消,完成,失败通知。不支持线程执行的前后顺序。

81. Parallel简化同步状态

82. 并行

第二部分 架构篇

第7章  成员设计

90.

<1 . 不要为抽象类提供公开的构造方法,抽象类设计只是为了继承,而不是用于生成实例对象

 <2. 可见字段应该重构为属性,属性和字段的区别:一个是方法,一个是字段

 <3. 谨慎把数组或者集合做为属性

 <4. 构造方法应初始化主要属性和字段。【一个猫生下来就已经具有尾巴了】

 <5. 区别对待override和new.[new 重写覆盖了父类方法,至关于该类中的一个新方法,和父类中的方法没有一点关系]

 <6. 避免在构造方法中调用虚方法

 <7. 成员优先考虑公开基类型或者接口

 <8 . 用params减小重复参数

 <9. 重写时不该该使用子类参数

建议100: 静态方法和实例方法没有区别

101. 使用扩展方法,向现有类型“添加”方法

 

第八章 类型设计

 

103. 区分组合和继承的应用场合

组合; 在新类A中声明 类B,C,D的实例。【有一个的概念】

107. 区分静态类和单例

静态类不是一个真正的对象,可是单例类时一个对象。

109. 谨慎使用嵌套类

当某一个类须要访问另外一个类型的私有成员时,才实现为嵌套类

111. 避免双重耦合【常见的解耦就是提炼接口】

112. 把现实世界中的对象抽象为类【猫,狗】,将可复用对象圈起来就是命名空间【植物,动物】

 

第9章 安全性设计

考虑可能出现的最大值:定义加工资,最大值。checked{} 关键字i行核实,会主动抛出异常

114. MD5 再也不安全 【穷举法破解】

115. HASH 检验文件是否被纂改

116. 避免非对称算法加密

117. ......

 

编码规范和习惯:

 <1.  Company.Component 命名空间命名

 <2. 考虑命名空间使用复数,System.Books   不要System.AllBook

 <3. 用名词和名词组给类型命名    推荐ScoreManager    不要SoreManage

 <4. 考虑让派生类的名字以基类名字做为后缀

 <5. 泛型类型参数要以T做为前缀

 <6. 以复数命名枚举类型,以单数命名枚举元素【Week  不要Day】

 <7. 用camelCasing命名私有字段和局部变量

 <8. 常量如下划线的方式   TASK_STATE_CANCELED    s_ 静态变量   

 <9. 考虑使用确定性的短语命名bool属性  IsEnabled 

 <10.优先使用后缀做为一个类型的信版本,不到不得已并不推荐  Book1   Book2

 <11. 委托和事件加上上级后缀 HttpDelegate()

 <12. 事件处理器函数采用组合式命名: Button_SizeChanged()

 

代码整洁: 

代码整洁的要求之一,就是尽可能减小代码。如省略默认的访问修饰符

<1. 使用表驱动法避免过长的if和switch分支

 

<2. 使用匿名方法,Lambda表达式代替方法   若是方法体小于3行

<3. 使用事件访问器替换公开的事件成员变量

    

相关文章
相关标签/搜索