《必须知道的.net》读书笔记(三)

 

第三部分格局——.Net面面俱到

第1章深入浅出——关键字的秘密

1.1把new说透

1引言

     new 一个 class 对象和 new 一个 struct 或者 enum 有什么不同? 

     new 在.NET 中有几个用途,除了创建对象实例,还能做什么? 

     new 运算符,可以重载吗? 

     范型中,new 有什么作用? 

     new 一个继承下来的方法和 override 一个继承方法有何区别? 

     int i 和 int i = new int()有什么不同? 

2基本概念

子类修饰符,泛型约束,多态

3深入浅出

new 一个 int 时,new 运算符用于初始化其值为 0。

new 运算符不可重载。

1.2base和this

1引言

base 常用于哪些方面?this 常用于哪些方面?

2基本概念

3深入浅出

尽量少用或者不用 base 和 this。

在静态成员中使用 base 和 this 都是不允许的。

base 是为了实现多态而设计的。

1.3深入浅出关键字——using全接触

1引言

引入命名空间,创建别名,强制资源清理

using Namespace;

using alias = namespace | type;

 

第2章颠峰对决——走出误区

2.1struct和class

struct 只能声明带参数构造函数,且不能声明析构函数。

struct 为抽象,也不能声明抽象函数。

struct 可以重载 System.Object 的 3 个虚方法,Equals()、ToString()和GetHashTable()。

我们可以简单的理解,class 是一个可以动的机器,有行为,有多态,有继承;而 struct 就是个零件箱,组合了不同结构的零件。

2.2历史纠葛——特性和属性

1引言

Attribute

2概念引入

公共语言运行时允许添加类似关键字的描述声明,叫做 attributes, 它对程序中的元素进行标注,如类型、字段、方法和属性等。

定制特性 attribute,本质上是一个类,其为目标元素提供关联附加信息,并在运行期以反射的方式来获取附加信息。

定制特性主要应用在序列化、编译器指令、设计模式等方面。类似于注释,但是可以在运行期以反射的方式获得。

3通用规则

attibute 实例,是在编译期进行初始化,而不是运行期。

C#允许以指定的前缀来表示特性所应用的目标元素。

定制特性类型,必须直接或者间接的继承自 System.Attribute 类,而且该类型必须有公有构造函数来创建其实例。

所有自定义的特性名称都应该有个 Attribute 后缀,这是习惯性约定。

定制特性也可以应用在其他定制特性上,这点也很好理解,因为定制特性本身也是一个类,遵守类的公有规则。

定制特性不会影响应用元素的任何功能,只是约定了该元素具有的特质。

定制特性可以应用在某些设计模式中,如工厂模式。

定制特性还常用于位标记,非托管函数标记、方法废弃标记等其他方面。

4特性的应用

AttributeUsage

Flags

DllImport

Serializable

Conditional

Description

DefaultValue

Category

ReadOnly

BrowerAble

5经典事例

(TestAttribute)Attribute.GetCustomAttribute(mInfo, typeof(TestAttribute))

object obj = Activator.CreateInstance(typeof(Mytest)); 

MethodInfo mi = tp.GetMethod("SayHello");

mi.Invoke(obj, null);

2.3对抽象编程——接口和抽象类

1引言

接口是包含一组虚方法的抽象类型,其中每一种方法都有其名称、参数和返回值。

CLR 允许接口可以包含事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数。但是注意:C#中不能包含任何静态成员。

2相同点和不同点

接口可以用于支持回调;抽象类不能实现回调,因为继承不支持。

接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。

 

面向对象思想的一个最重要的原则就是:面向接口编程。

抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。

接口着重于 CAN-DO 关系类型,而抽象类则偏重于 IS-A 式的关系;

接口多定义对象的行为;抽象类多定义对象的属性;

 “接口不变”,是应该考虑的重要因素。所以,在由接口增加扩展时,应该增加新的接口,而不能更改现有接口。

在接口中,所有的方法都默认为 public。

如果预计会出现版本问题,可以创建“抽象类”。

在抽象方法声明中不能使用 static 或 virtual 修饰符。

3经典示例

System.Collections

System.IO.Stream

2.4恩怨情仇——is和as

1引言

任何类型都可以安全的转换为其基类类型,可以由隐式转换来完成;

任何类型转换为其派生类型时,必须进行显示转换,转换的规则是:(类型名)对象名;

使用 GetType 可以取得任何对象的精确类型;

基本类型可以使用 Covert 类实现类型转换;

除了 string 以外的其他类型都有 Parse 方法,用于将字符串类型转换为对应的基本类型;

值类型和引用类型的转换机制称为装箱(boxing)和拆箱(unboxing)。

2原理及示例

is 的规则如下:

     检查对象类型的兼容性,并返回结果,true 或者 false; 

     不会抛出异常; 

     如果对象为 null,则返回值永远为 false。

as 的规则如下:

     检查对象类型的兼容性,并返回结果,如果不兼容就返回 null; 

     不会抛出异常; 

     如果结果判断为空,则强制执行类型转换将抛出 NullReferenceException 异常。

as 操作符在执行效率上更胜一筹。

2.5貌合神离——复写和重载

1引言

Overload参数列表不同或泛型不同;override重写virtual或abstract

2多态中的应用

覆写实现了运行时的多态性,而重载实现了编译时的多态性。

 

第8章本来面目——框架诠释

8.1万物归宗System.Object

1引言

l  通过 GetType 方法,获取对象类型信息。

l  通过 Equals、ReferenceEquals 和==,实现对象判等。

l  通过 ToString 方法,获取对象字符串信息 ,默认返回对象类型全名。

l  通过 MemberwiseClone 方法,实现对象实例的浅拷贝。

l  通过 GetHashCode 方法,获取对象的值的散列码。

l  通过 Finalize 方法,在垃圾回收时进行资源清理。

2初识

System.Object 主要包括了 4 个公用方法和 2 个受保护方法2个静态方法。

3分解

IFormattable:string ToString(string format, System.IFormatProvider formatProvider);

GetType 返回的是一个 System.Type 或其派生类的实例。而该实例对象可以通过反射获取类型的元数据信息。

Type.GetType静态方法和 typeof 运算符也能完成同样的操作,不过在应用上有些区别。

l  Type.GetType 是非强类型方法;而 typeof 运算符支持强类型。

l  Type.GetType 支持运行时跨程序集反射,以解决动态引用;而 typeof 只能支持静态引用。

 

l  利用 System.Reflection.Assembly 的非静态方法 GetType 或 GetTypes。

l  利用 System.Reflection.Module 的非静态方法 GetType 或 GetTypes。

4意义

l  实现自上而下的单根继承。

l  System.Object 是一切类型的最终基类,也就意味着.NET 的任何变量都是 System.Object 的实例,这种机制提供了不同类型之间进行交互通信的可能。

8.2规则而定——对象判等

1引言

值相等,引用相等

自反性,对称性,传递性

2本质分析

Equals 静态方法的执行结果,依次取决于三个条件:

l  是否为同一实例。

l  是否都为 null。

l  第一个参数的 Equals 实现。

ReferenceEquals 方法用于判断两个引用是否指向同一个对象。而以 ReferenceEquals 方法比较值类型,必然false。

默认情况下,Equals虚方法和 ReferenceEquals 方法是一样的。Equals 的比较结果取决于类的创建者是如何实现的,而非统一性约定。

操作符“==”在值类型情况下表示是否值相等,由值类型的根类 System.ValueType  提供了实现;而在引用类型情况下表示是否引用相等,而“!=”操作符与“==”语义类似。当然也有例外,System.String 类型则以“==”来处理值相等。

3覆写Equals方法

必须遵循三个等价原则:自反、传递和对称,这是实现 Equals 的通用契约。

首先,检测 obj 是否为 null,如果是则必然不相等;然后,以 ReferenceEquals 来判等是否引用相等,这种办法比较高效,因为引用相等即可以推出值相等;然后,再进行类型判断,不同类型的对象一定不相等;最后,也是最复杂的一个过程,即对对象的各个成员进行比较,引用类型进行恒定性判断,值类型进行恒等性判断。

4与GetHashCode方法同步

GetHashCode 的行为依赖于 Equals 方法进行判断,在覆写Equals 方法时,也必须覆写 GetHashCode,以同步二者在语义上的统一。

5规则

l  如果覆写了 Equals 虚方法,则必须重新实现 GetHashCode 方法,使二者保持同步。

l  禁止从 Equals 方法或者“==”操作符抛出异常,应该在 Equals 内部首先避免 null 引用异常,要么相等要么不等。

l  ReferenceEquals 方法主要用于判别两个对象的唯一性,比较两个值类型则一定返回 false。

l  ReferenceEquals 方法比较两个 System.String 类型的唯一性时,要注意 String 类型的特殊性:字符串驻留。

l  实现 ICompare 接口的类型必须重新实现 Equals 方法。

l  值类型最好重新实现 Equals 方法和重载==操作符,因为默认情况下实现的是引用相等。

8.3如此特殊——大话String

1引言

l  创建特殊性:String 对象不以 newobj 指令创建,而是 ldstr 指令创建。在实现机制上,CLR 给了特殊照顾来优化其性能。

l   String 类型是.NET 中不变模式的经典应用,在 CLR 内部由特定的控制器来专门处理 String 对象。

l  应用上,String 类型表现为值类型语义;内存上,String 类型实现为引用类型,存储在托管堆中。

l  两次创建内容相同的 String 对象可以指向相同的内存地址。

l  String 类型被实现为密封类,不可在子类中继承。

l  String 类型是跨应用程序域的,可以在不同的应用程序域中访问同一 String 对象。

2字符串创建

System.String 只提供了数个接受 Char*、Char[]类型的构造函数,典型的new方法创建会出现编译错误。

3字符串恒定性

指字符串一经创建,就不可改变。

String 类型是不变模式在.NET 中的典型应用,String 对象从应用角度体现了值类型语义,而从内存角度实现为引用类型存储,位于托管堆。

l  保证对 String 对象的任意操作不会改变原字符串。

l  恒定性还意味着操作字符串不会出现线程同步问题。

l  恒定性一定程度上,成就了字符串驻留。

public sealed class String : IComparable, ICloneable, IConvertible, Icomparable <string>, IEnumera

ble<char>, IEnumerable, IEquatable<string>

4字符串驻留

l  一方面,CLR 必须提供特殊的处理结构,来维护对相同字符串共享内存的机制。

l  另一方面,CLR 必须通过查找来添加新构造的字符串对象到其特定结构中。

CLR 内部维护了一个哈希表(Hash Table)来管理其创建的大部分 string 对象。其中,Key 为 string 本身,而 Value 为分配给对应的 string 的内存地址。

对于动态生成的字符串,因为没有添加到 CLR 内部维护的哈希表而使字符串驻留机制失效。

可以手工启用字符串驻留机制:

public static string Intern(string str);

public static string IsInterned(string str);

字符串驻留是进程级的,可以跨应用程序域(AppDomain)而存在。垃圾回收不能释放哈希表中引用的字符串对象,只有进程结束这些对象才会被释放。

5字符串操作典籍

字符串与基本类型转换:ToString;Parse,TryParse,Convert.To[Type]

转义字符\,字面字符@

6补充的礼物StringBuilder

基于性能的考虑,我们应该尽可能使用 StringBuilder 来动态创建字符串,然后以 ToString 方法将其转换为 String 对象应用。

ToString,Append,AppendFormat,Insert,Replace等。

8.4简易不简单——认识枚举

1引言

除了良好的可读性、易于维护、强类型的优点之外,性能的考虑也占了一席之地。

2枚举类型解析

所有枚举类型都隐式而且只能隐式地继承自 System.Enum 类型,System.Enum 类型是继承自 System.ValueType 类型唯一不为值类型的引用类型。

public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible

枚举类型是值类型,分配于线程的堆栈上,自动继承于 Enum 类型,但是本身不能被继承;Enum 类型是引用类型,分配于托管堆上,Enum 类型本身不是枚举类型,但是提供了操作枚举类型的共用方法。

在本质上枚举就是一个常数集合,各个成员常量相当于类的静态字段。

l  枚举定义时可以声明其基础类型

l  枚举成员是枚举类型的命名常量,任意两个枚举常量不能具有同样的名称符号,但是可以具有相同的关联值。

l  枚举成员会显式或者隐式与整数值相关联

l   枚举成员可以自由引用其他成员的设定值,但是一定注意避免循环定义

l  枚举是一种特殊的值类型,不能定义任何的属性、方法和事件,枚举类型的属性、方法和事件都继承自 System.Enum 类型。

l  枚举类型是值类型,可以直接通过赋值进行实例化

l  枚举可以进行自增自减操作

3枚举种种

整数转换必须显式;也可以通过Enum.Parse(typeof(enumtype),value)

ToString(“G”)普通格式;”X”16进制;”D”10进制

装箱为父类引用类型或接口

Enum.GetNames(typeof(enumtype)), Enum.GetValues(typeof(enumtype))

Enum.IsDefine,Enum.GetUnderlyingType

4位枚举

[Flags]

l   Enum.IsDefined 方法不能应对位枚举成员

l  Flags 特性影响 ToString、Parse 和 Format 方法的执行过程和结果。

l  如果不使用 FlagsAttribute 特性来标记位枚举,也可以在 ToString 方法中传入“F”格式来获得同样的结果

l  在位枚举中,应该显式的为每个枚举成员赋予有效的数值,并且以 2 的幂次方为单位定义枚举常量,这样能保证实现枚举常量的各个标志不会重叠。

5规则和意义

l  枚举类型是强类型的,从而保证了系统安全性。

l  枚举类型的默认值为 0,因此,通常给枚举成员包含 0 值是有意义的

l  枚举的声明类型,必须是基于编译器的基元类型,而不能是对应的 FCL 类型

8.5一脉相承——委托、匿名方法和Lambda表达式

1引言

委托,实现了类型安全的回调方法。在.NET 中回调无处不在,所以委托也无处不在,事件模型建立在委托机制上,Lambda 表达式本质上就是一种匿名委托。

2解密委托

public delegate void CalculateDelegate(Int32 x, Int32 y);

委托并不等同与方法,而是一个引用类型,类似于 C++中的函数指针

委托表示了对其回调方法的签名,可以将方法当作参数进行传递,并根据传入的方法来动态的改变方法调用。只要为委托提供相同签名的方法,就可以与委托绑定。

 

可以很容易想到将多个方法绑定到一个委托变量,在调用一个方法时,可以依次执行其绑定的所有方法,这种技术称为多播委托。

多播委托按照委托链顺序调用所有绑定的方法。

以+=和-=操作符分别进行绑定和解除绑定的操作。

+=和-=操作分别调用了 Deleagate.Combine 和 Deleagate.Remove 方法。

多播委托返回值一般为 void,委托类型为非 void 类型时,多播委托将返回最后一个调用的方法的执行结果。

 

委托在本质上仍然是一个类,如此简洁的语法正是因为 CLR 和编译器在后台完成了一系列操作。

委托本质上仍旧是一个类,该类继承自 System.MulticastDelegate 类,该类维护一个带有链接的委托列表,在调用多播委托时,将按照委托列表的委托顺序而调用的。还包括一个接受两个参数的构造函数和 3 个重要方法:BeginInvoke、EndInvoke 和 Invoke。

构造函数,它包括了两个参数:第一个参数表示一个对象引用,它指向了当前委托调用回调函数的实例,即指向一个 当前类 对象;第二个参数标识了回调方法,也就是 绑定 方法。

真正执行调用的是 Invoke 方法。

3委托和事件

事件是对委托的封装。事件相当于一个特殊定义的委托变量。

public class Calculator

{

    //定义一个 CalculateEventArgs,

    //用于存放事件引发时向处理程序传递的状态信息

    public class CalculateEventArgs: EventArgs

    {

        public readonly Int32 x, y;

        public CalculateEventArgs(Int32 x, Int32 y)

        {

            this.x = x;

            this.y = y;

        }

    }

    //声明事件委托

    public delegate void CalculateEventHandler(object sender,CalculateEventArgs e);

    //定义事件成员,提供外部绑定

    public event CalculateEventHandler MyCalculate;

    //提供受保护的虚方法,可以由子类覆写来拒绝监视

    protected virtual void OnCalculate(CalculateEventArgs e)

    {

        if (MyCalculate != null)

        {

            MyCalculate(this, e);

        }

    }

    //进行计算,调用该方法表示有新的计算发生

    public void Calculate(Int32 x, Int32 y)

    {

        CalculateEventArgs e = new CalculateEventArgs(x, y);

        //通知所有的事件的注册者

        OnCalculate(e);

    } 

}

l  定义一个内部事件参数类型,用于存放事件引发时向事件处理程序传递的状态信息,EventArgs是事件数据类的基类。

l  声明事件委托,主要包括两个参数:一个表示事件发送者对象,一个表示事件参数类对象。

l  定义事件成员。

l  定义负责通知事件引发的方法,它被实现为 protected virtual 方法,目的是可以在派生类中覆写该方法来拒绝监视事件。

l  定义一个触发事件的方法,例如 Calculate 被调用时,表示有新的计算发生。

//定义事件处理程序

public class CalculatorManager

{

    //定义消息通知方法

    public void Add(object sender, Calculator.CalculateEventArgs e)

    {

        Console.WriteLine(e.x + e.y);

    }

    public void Substract(object sender, Calculator.CalculateEventArgs e)

    {

        Console.WriteLine(e.x - e.y);

    }

}

//实现事件处理

public class Test_Calculator

{

    public static void Main()

    {

        Calculator calculator = new Calculator();

        //事件触发者

        CalculatorManager cm = new CalculatorManager();

        //事件绑定

        calculator.MyCalculate += cm.Add;

        calculator.Calculate(100, 200);

        calculator.MyCalculate += cm.Substract;

        calculator.Calculate(100, 200);

        //事件注销

        calculator.MyCalculate -= cm.Add;

        calculator.Calculate(100, 200);

    }

}

上述实现过程实质是 Observer 模式在委托中的应用。

4匿名方法

匿名方法以内联方式放入委托对象的使用位置,而避免创建一个委托来关联回调方法,也就是由委托调用了匿名的方法,将方法代码和委托实例直接关联。

匿名方法和委托在 IL 层是等效的。

//匿名方法

        CalculateDelegate mySubstractDelegate = delegate(Int32 x, Int32 y)

        {

            Console.WriteLine(x - y);

        };

button1.Click += delegate

{

    MessageBox.Show("Hello world.");

};

5Lambda表达式

Lambda 表达式是 Functional Programming 的核心概念,现在 C# 3.0 中也引入了 Lambda 表达式来实现更加简洁的语法,并且为 LINQ 提供了语法基础。

class LambdaExpressionEx

{

    delegate void CalculateDelegate(Int32 x, Int32 y);

    public static void Main()

    {

        CalculateDelegate myDelegate = (x, y) => Console.WriteLine(x - y);

        myDelegate(100, 200);

    }

}

Lambda 表达式在本质上仍然是一个委托。

6规则

l  委托实现了面向对象的,类型安全的方法回调机制。

l  以 Delegate 作为委托类型的后缀,以 EventHandle 作为事件委托的后缀,是规范的命名规则。

l  多播委托返回值一般为 void,不推荐在多播委托中返回非 void 的类型。

l  匿名方法和 Lambda 表达式提供了更为简洁的语法表现,而这些新的特性主要是基于编译器而实现的,在 IL 上并没有本质的变化。

l  .NET 的事件是 Observer 模式在委托中的应用,并且基于.NET 规范而实现,体现了更好的耦合性和灵活性。

8.6直面异常

1引言

良好的系统设计必定有良好的异常处理机制来保证程序的健壮性和容错机制。

2为何而抛

对于异常的处理,基本有两种方式来完成:一种是异常形式,一种是返回值形式。

异常机制所具有的优势不可替代,主要表现为:

l  很多时候,返回值方式具有固有的局限性,例如在构造函数中就无法有效的应用返回值来返回错误信息,只有异常才能提供全面的解决方案来应对。

l  提供更丰富的异常信息,便于交互和调试,而传统的错误代码不能有效提供更多的异常信息和调试指示,在程序理解和维护方面异常机制更具优势。

l  有效实现异常回滚,并且可以根据不同的异常,回滚不同的操作,有效实现了对系统稳定性与可靠性的控制。

l  很好地与面向对象语言集成,在.NET 中异常机制已经很好地与高级语言集成在一起,以异常 System.Exception 类建立起的体系结构已经能够轻松应付各种异常信息,并且可以通过面向对象机制定义自己的特定异常处理类,实现更加特性化的异常信息。

l  错误处理更加局部化,错误代码更集中地放在一起,增强了代码的理解和维护,例如资源清理的工作完全交由 finally 子句来执行,不必花费过多的精力去留意其维护。

l  错误代码返回的信息内容有限而难于理解,一连串数字显然不及丰富的文字信息说明问题,同时也不利于快速地定位和修改需要调试的代码。

l  异常机制能有效应对未处理的异常信息,我们不可能轻易地忽略任何异常;而返回值方式不可能深入到异常可能发生的各个角落,不经意的遗漏就会造成系统的不稳定,况且这种维护方式显然会让系统开发人员精疲力竭。

l  异常机制提供了实现自定义异常的可能,有利于实现异常的扩展和特色定制。

异常机制是处理系统异常信息的最好机制与选择,异常是对程序接口隐含假设的一种违反。

异常处理将占用大量的系统资源和执行时间,因此建议尽可能的以处理流程来规避异常处理。

3从 try/catch/finally说起:解析异常机制

l  catch 子句可以带异常筛选器,也可以不带任何参数。

l  异常筛选器,可以指定一个异常变量,该变量将指向抛出的异常类对象,该对象记录了相关的异常信息,可以在 catch 子句内获取该信息。

l  finally 子句内,也可以抛出异常,但是应该尽量避免这种操作。

l  CLR 如果没有搜索到合适的异常筛选器,则说明程序发生了未预期的异常,CLR 将抛出一个未处理异常,应用程序应该提供对未处理异常的应对策略。

l  try 块内定义的变量对 try 块外是不可见的,因此对于 try 块内进行初始化的变量,应该定义在 try 块之前,否则 try 块外的调用将导致编译错误。

4 .NET 系统异常类

Exception 类提供了所有异常类型的基本属性与规则,例如:

l  Message 属性,用于描述异常抛出原因的文本信息。

l  InnerException 属性,用于获取导致当前异常的异常集。

l  StackTrack 属性,提供了一个调用栈,其中记录了异常最初被抛出的位置,因此在程序调试时非常有用。

l  HResult 受保护属性,可读写 HRESULT 值,分配特定异常的编码数值,主要应用于托管代码与非托管代码的交互操作。

HelpLink 用于获取帮助文件的链接,TargetSite 方法用于获取引发异常的方法。

实现 ISerializable接口方法 GetObjectData 值得关注,异常类新增字段必须通过该方法填充 SerializationInfo,异常类进行序列化和反序列化必须实现该方法。

5 定义自己的异常类

//Serializable 指定了自定义异常可以被序列化

[Serializable]

public class MyException : Exception, ISerializable

{

//自定义本地文本信息

//重写只读本地文本信息属性

    public override string Message

    {

        get 

        {

            string msgBase = base.Message;

            return myMsg == null ? msgBase : msgBase + myMsg;

        }

    }

    //实现基类的各公有构造函数

    //为新增字段实现构造函数

    public MyException(string message, string myMsg)

        : this(message)

    {

        this.myMsg = myMsg;

    }

    public MyException(string message, string myMsg, Exception innerException)

        : this(message, innerException)

    {

        this.myMsg = myMsg;

    }

    //用于序列化的构造函数,以支持跨应用程序域或远程边界的封送处理

    protected MyException(SerializationInfo info, StreamingContext context) 

        : base(info, context)

    {

        myMsg = info.GetString("MyMsg");

    }

    //重写基类 GetObjectData 方法,实现向 SerializationInfo 中添加自定义字段信息

    public override void GetObjectData(SerializationInfo info, StreamingContext context)

    {

        info.AddValue("MyMsg", myMsg);

        base.GetObjectData(info, context);

}

l  System.Exception 类型提供了三个公有构造函数,在自定义类型中也应该实现三个构造函数,并且最好调用基类中相应的构造函数;如果自定义类型中有新的字段要处理,则应该为新的字段实现新的构造函数来实现。

l  所有的异常类型都是可序列化的,因此必须为自定义异常类添加 SerializableAttribute 特性,并实现 ISerializable 接口。

l  以 Exception 作为异常类名的后缀,是良好的编程习惯。

l  如果自定义异常没有必要实现子类层次结构,那么异常类应该定义为密封类(sealed),以保证其安全性。

6异常法则

l  尽可能以逻辑流程控制来代替异常

l  将异常理解为程序的错误,显然曲解了对异常本质的认识。

l  对异常形成文档,详细描述关于异常的原因和相关信息,是减少引发异常的有效措施。

l  异常提示应该准确而有效,提供丰富的信息给异常查看者来进行正确的判断和定位。

l  异常必须有针对性,盲目地抛出 System.Exception 意味着对于异常的原因是盲目的,而且容易造成异常被吞现象的发生。

l  尽量避免在 Finally 子句抛出异常。

l  应该避免在循环中抛出异常。

 

第9章接触泛型

C#除可单独声明泛型类型(包括类与结构)外,也可在基类中包含泛型类型的声明。但基类如果是泛型类,它的类型参数要么已实例化,要么来源于子类(同样是泛型类型)声明的类型参数。

泛型类型的成员可以使用泛型类型声明中的类型参数。

// 泛型接口的类型参数要么已实例化,

// 要么来源于实现类声明的类型参数

泛型委托支持在委托返回值和参数上应用参数类型,这些参数类型同样可以附带合法的约束。

C#泛型机制只支持“在方法声明上包含类型参数”——即泛型方法

泛型方法既可以包含在泛型类型中,也可以包含在非泛型类型中。

C#泛型机制不支持在除方法外的其他成员(包括属性、事件、索引器、构造器、析构器)的声明上包含类    型参数,但这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数。

泛型方法重写的时候,不能写约束,也不添加新的约束,只能继承父类的约束。只需要写方法的签名。

 

“显式约束”由 where 子句表达,可以指定“基类约束”,“接口约束”,“构造器约束”“值类型/引用类型约束”共四种约束。

“显式约束”并非必须,如果没有指定“显式约束”,泛型类型参数将只能访问 System.Object 类型中的公有方法。

C#的泛型支持包括类、结构、接口、委托共四种泛型类型,以及方法成员。

 

第10章认识全面的null

1什么是null

在.NET 中,null 表示一个对象引用是无效的。

表示一个引用类型变量声明但未初始化的状态。

object obj 和 object obj = null 的区别:主要体现在编译器的检查上。后者通过 ldnull 指令推进一个空引用给 evaluation stack。

     null 为引用类型变量的默认值,为引用类型的概念范畴。 

     null 不等同于 0,"",string.Empty。 

     引用 is 或 as 模式对类型进行判断或转换时,需要做进一步的 null 判断。

     判断一个变量是否为 null,可以应用==或!=操作符来完成。 

     对任何值为 nul 的 l 变量操作,都会抛出 NullReferenceException 异常。

2Nullable<T>可空类型

int? i = null;

Nullable<int> i = null;

不允许使用嵌套的可空类型,例如 Nullable<Nullable<T>>。

Nullable<T>和 T?是等效的。

对可空类型执行 GetType 方法,将返回类型 T,而不是 Nullable<T>。

3??运算符

??运算符,又称为 null-coalescing operator,如果左侧操作数为 null,则返回右侧操作数的值, 如果不为 null 则返回左侧操作数的值。

4Null Object模式

返回 null object 而不是 null 值。IsNull 属性用于提供统一判定 null 方式。

添加一个实现INullable的接口实现类,并实现对象类的接口;对对象的调用通过接口实现。

也可通过C#3.0ExtensionMethod方式实现

 

第4部分.net学习

CLR 运行机制,深刻的认识内存管理,类型系统,异常机制,熟悉 FCL 基本架构,学习 c#语言基础,认识 MSIL、元数据、Attribute、反射、委托等等,当然还包括面向对象和设计架构,都是必不可少的基础内容。

书籍:

Essential .NET,一本圣经,Don Box, Chris Sells

Applied Microsoft .NET Framework Programming,.NET 世界的唯一经典,Jeffrey Richter

Pracical .NET2 and C#2,.NET 领域的百科全书,Patrick Smacchia

Garbage Collection: Algorithms for Automatic Dynamic Memory Management,内存管理方面,Richard Jones, Rafael D Lins

Thinking in Java,带来的不仅仅是 Java

.NET 设计规范--.NET 约定、惯用法与模式

 

Elisabeth Freeman. Head First Design Patterns

设计模式-可复用面向对象软件的基础

Refactoring: Improving the Design of Existing Code

敏捷软件开发:原则、模式与实践

软件设计精要与模式

 

     面向对象的基本内容:类、对象、属性、方法、字段。 

     面向对象的基本要素:封装、继承、多态,我再外加一个接口。 

     设计原则:接口隔离、单一职责、开放封闭、依赖倒置、Liskov 替换

     设计模式:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值