唐大仕 dstang2000@263.net http://www.dstang.com 第4章 C#高级特性,异常,调试 唐大仕 dstang2000@263.net http://www.dstang.com
本章内容 4.1 程序的组织 4.2 委托与事件 4.3 运算符重载 4.4 异常处理 4.5 Attribute 4.1 程序的组织 4.2 委托与事件 4.3 运算符重载 4.4 异常处理 4.5 Attribute 4.6 C#语言中的其他成分 4.7 程序的调试
4.1 程序的组织 名字空间 程序的逻辑组织 嵌套类型 类中嵌套类型 程序集 程序的物理组织
名字空间 1.名字空间的概念 2.名字空间的声明 3.名字空间的导入 4.使用别名 逻辑划分;避免名字冲突 namespace xxx.xxxx { } 可嵌套 3.名字空间的导入 using xxx.xxxx; 4.使用别名 using 别名 = 名字空间或类名; NamespaceUsing.cs
嵌套类型 嵌套类型的概念 嵌套类型的可访问性 类型中的类型 class A{ public class B{ public struct C{} } } new A.B.C(); 嵌套类型的可访问性 受各个层次的限制 NestedAccessibility.cs
程序集 模块(module) 程序集(assembly) 在VS.NET上引用程序集 在项目上点右键,添加引用
csc /target:mod /out:Add.mod Add.cs csc /target:mod /out:Multi.mod Multi.cs al /target:library /out: MyLibrary.dll Add.mod Multi.mod csc /target:exe /out:MyClient.exe /reference:MyLibrary.dll MyClient.cs
大致上: 委托-------函数指针 事件-------回调函数 4.2 委托与事件 大致上: 委托-------函数指针 事件-------回调函数
委托 委托的声明 委托的实例化 委托的调用 委托变量名(参数列表 ) d2(8.9) DelegateIntegral.cs public delegate double MyDelegate ( double x ); 委托的实例化 MyDelegated d2 = new MyDelegate( obj.myMethod ); 委托的调用 委托变量名(参数列表 ) d2(8.9) DelegateIntegral.cs
委托示例 DelegatePlotFun.cs
委托的合并 委托的合并----多播MultiCastDelegate 运算符 + - += -= 一个委托实例中可以“包含”多个函数 调用委托,就是调用其中多个函数 多个函数间的先后顺序是没有意义的 运算符 + - += -= 动态地增减其中的函数 提高了程序的灵活性 DelegateMultiTest.cs Delegate温度.cs
委托的转换与相等 委托的转换 委托的相等 按声明的名称判断 以下两个不能互相转换或加减 按内容(即其中“包含的函数”)来判断 delegate void D( int a ); delegate void E( int a ); 委托的相等 按内容(即其中“包含的函数”)来判断 DelegateEquals.cs
事件 事件的声明 事件的注册与移除 事件的发生 public event 委托名 事件名; 事件名 += 或 -= 事件名(参数列表) 事件名 += 或 -= 在事件所在类的外面,只能用以上两个运算符 事件的发生 事件名(参数列表) 相当于回调所注册的函数
事件与委托的关系 事件有点像委托类型的实例 事件比委托实例更复杂:事件存取器 事件一定有相关的委托类型 与委托实例一样,事件也“包含”多个函数 事件的运算符受更多限制(+=或-=) 事件比委托实例更复杂:事件存取器 修饰符 event 委托类型名 事件名 { add{ e += value; } remove{ e -= value; } } EventButtonForm.cs
使用事件的例子 网络爬虫程序中通知调用者下载开始、结束及下载进度 EventWhenDownload.cs
综合示例 使用C#多种语法要素(特别是event及Exception) 参见 BankSystem-v1-v2
csharp语言新特性与事件 C#2.0 引入泛型 C#3.0 引入Lambda及Linq C#4.0 更多的动态特性dynmaic
泛型(Generic) List<Book> books=new List<Book>(); Book book = books[0]; //以前要用强制类型转换 ArrayList books = new ArrayList(); Book book = (Book) books[0];
匿名方法 delegate(参数){ 方法体} 可以当一个匿名方法 可以被隐式转换为一个兼容的委托类型 new Thread( new ThreadStart( delegate(){ …….} ) ); 可以被隐式转换为一个兼容的委托类型 new Thread(delegate(){ …….} );
Lamda表达式 相当于匿名方法的简写 (参数)=>{语句或表达式}
Linq 常见的形式 相当于 from c in customers where c.Age>10 orderby c.Name select new {c.Name, c.Phone} 相当于 customers .Where(c=> c.Age>10) .OrderBy(c => c.Name). .Select(c => new { c.Name, c.Phone })
示例 在不同C#版本中使用delegate 参见:MethodDelegateLamda
4.3 运算符重载
运算符 使用运算符的例子 运算符有时比方法名更直观 运算符重载有一些限制 this.Location += new Size(10,10); TimeSpan diff = date2 – date1; String s1, s2; … if( s1==s2) 运算符有时比方法名更直观 如 两个复数用 a+b 比 a.Add(b)更直观 但要慎用 运算符重载有一些限制 如成对,如类型要求,如有的不能重载 更详细的内容,请参见文档
运算符的声明 一元运算符声明的形式如下: 二元运算符声明的形式如下: 类型转换运算符声明的形式如下: OperatorComplex.cs public static 类型 operator 一元运算符 ( 类型 参数名 ){ …… } 二元运算符声明的形式如下: public static 类型 operator 二元运算符 ( 类型 参数名,类型 参数名) { …… } 类型转换运算符声明的形式如下: public static implicit operator 类型 ( 类型 参数名) { …… } public static explicit operator 类型 ( 类型 参数名) { …… } OperatorComplex.cs
4.4 异常处理
异常处理 异常可以以两种不同的方式引发: 首先介绍一下throw语句。throw语句抛出一个异常的语法为: C#语句和表达式执行过程中激发了某个异常的条件,使得操作无法正常结束,从而引发异常。例如整数除法操作分母为零时将抛出一个异常。 首先介绍一下throw语句。throw语句抛出一个异常的语法为: throw expression 带有表达式的throw语句抛出的异常是在计算这个表达式时产生的。异常由try语句来处理的 try语句提供了一种机制来捕捉执行过程中发生的异常。以下是它的三种可能的形式: try-catch try-finally try-catch-finally
异常的概念 C#中的异常处理 System.Exception类 try{ } catch(Exception e){ } finally{ } System.Exception类 public Exception(); public Exception(string s); Message属性 StackTrace属性
几种常用的异常类 System.OutOfMemoryException System.StackOverflowException System.NullReferenceException System.TypeInitializationException System.InvalidCastException System.ArrayTypeMismatchException System.IndexOutOfRangeException System.MulticastNotSupportedException System.ArithmeticException System.DivideByZeroException System.OverflowException
捕获和处理异常 抛出异常 捕获异常 throw new SomeException(); try{ } catch(AException e1){ } catch(BException e2){ } catch(更一般的Exception e){ } finally{ } 注:catch{}表示捕获所有种类的异常 ExceptionIndexOutOf.cs ExceptionSimple.cs
创建用户自定义异常类 从Exception或ApplicationException继承 重抛异常 异常链接 throws; throw new Excepiton( “msg”, e ); 这里e称为内部异常 InnerException属性 使得外部能进一步知道内部的异常原因 ExceptionInner.cs
算术溢出与checked 对溢出进行检查 对溢出异常进行捕获 对整个程序 csc /checked XXXX.cs 对部分程序 针对表达式: checked(表达式) 及 uncheckd(表达式) 针对块语句: checked{……} 及 uncheckd{……} 对溢出异常进行捕获 try{ } catch( OverflowException e ) { } CheckedTest.cs
4.5 Attribute
使用Attribute的例子 用在类上的 用在方法上的 [Serializable] public sealed class String : IComparable, Icloneable, IConvertible, Ienumerable 用在方法上的 [STAThread] static void Main()
Attribute Attribute是与类、结构、方法等元素相关的额外信息, 是对元信息的扩展。 [HelpUrl(“http://SomeUrl/APIDocs/SomeClass”)] class SomeClass { [WebMethod] void GetCustomers() { … } string Test([SomeAttr] string param1) {…} } Blue box here and on next slide
使用系统定义的Attribute 使用Attribute的一般方式 示例 在程序集、类、域、方法等前面用[]表示 可以省略“Attribute”几个字母,只写xxxxx 可以带参数 位置参数 (相当于构造方法带的参数) 命名参数(域名或属性名=值) 示例 在Main()方法使用[STAThread] 使用“过时”:AttributeObsolete.cs 使用“条件”:AttributeConditional.cs 在结构上、枚举上使用:StructLayout,Flag 在程序集级别应用Attribute [assembly: AssemblyCompany("")]
自定义Attribute 1.声明Attribute类 2.使用Attribute类 3.通过反射访问属性 AttributeHelp.cs
Attributes are classes Attached to types and members Attributes can be queried at runtime class HelpUrlAttribute : System.Attribute { public HelpUrlAttribute(string url) { … } … } [HelpUrl(“http://SomeUrl/APIDocs/SomeClass”)] class SomeClass { … } Type type = Type.GetType(“SomeClass”); object[] attributes = type .GetCustomAttributes();
4.6 C#语言中的其他成分
编译预处理 1.标识符声明 2.条件处理 3.信息报告 4.行号标记 #define定义一个标识符; #undef“取消定义”一个标识符。 #if, #elif, #else, #endif 3.信息报告 #error 和 #warning 4.行号标记 #line 行号 "文件名"
unsafe及指针 1.unsafe 2.fixed及指针 3.sizeof运算符 4.stackalloc 用于修饰类、方法等 在栈上分配的内存,而不是在堆上,因此不会担心内存被垃圾回收器自动回收。 UnsafeCopy.cs
C# And Pointers Developers sometime need total control Performance extremes Dealing with existing binary structures Advanced COM Support, DLL Import C# “unsafe” = a limited “inline C” Pointer types, pointer arithmetic Unsafe casts Declarative pinning (fixed statement) C# developers have headroom Unsafe note: Unsafe code can’t be verified, and therefore needs full trust to execute; can’t be used in a download scenario.
Unsafe Example class FileStream: Stream { int handle; [dllimport("kernel32", SetLastError=true)] static extern unsafe bool ReadFile(int hFile, void* lpBuffer, int nBytesToRead, int* nBytesRead, Overlapped* lpOverlapped); public unsafe int Read(byte[] buffer, int index, int count) { int n = 0; fixed (byte* p = buffer) { ReadFile(handle, p + index, count, &n, null); } return n; This is the first view of defining a dllimport, so it pays to discuss it here… Important point on fixed: Fixed sets a flag on the object so that it won’t be moved if a garbage collection occurs. This is very low cost, since there is no effect unless a GC occurs inside the fixed block.
其他关键字 1.lock 2. volatile 多线程程序中,lock可以将某个对象加锁
C#几个语法的小结 类型声明 类的成员
类型声明 类型声明是C#程序的主体,它可以位于名字空间中,也可以是嵌套的类型。 类型声明包括以下几种: 1) 类 class 2) 结构 struct 3) 接口 interface 4) 枚举 enum 5) 委托 delegate
类的成员 1) 常数(const) 2) 域(field) 3) 方法(method) 4) 属性(property) 它代表了与类相关的常数数据。 2) 域(field) 它是类中的变量。 3) 方法(method) 它实现了可以被类实现的计算和行为。 4) 属性(property) 它定义了命名的属性和与对这个属性进行读写的相关行为。 5) 事件(event) 它定义了由类产生的通知 6) 索引(indexer) 它允许类的实例通过与数组相同的方法来索引。
类的成员(续) 7) 运算符(operator) 8) 实例构造函数(instance constructor) 它定义了可以被应用于类的实例上的表达式运算符。 8) 实例构造函数(instance constructor) 它执行需要对类的实例进行初始化的动作。 9) 析构函数(destructor) 类的实例被清除时实现的动作(结构不能有析构函数)。 10) 静态构造函数(static constructor) 它执行对类本身进行初始化的动作。 11) 类型(type) 它代表位于类中的类型。
4.7 程序的调试
错误的种类 代码的错误通常可以分成三大类 语法错误 运行错误 逻辑错误 其中语法错误比较容易排除,也是一种低级的错误。运行错误和逻辑错误需要靠经验、调试工具以及不断的深入代码来排除。
语法错误 此类型的错误通常发生在程序员对语言本身的熟悉度不足 如if之后忘了加上大括号、For语句少了分号。还有比如定义一个变量是Student,但是在引用的时候不小心就变成studnet了。还有一种情况是调用函数错误的语法。 例如调用SubString函数少传了一个参数。使用Visual Studio.NET开发工具,多数的错误会在编辑程序的时候发现。
逻辑错误 逻辑错误(Logic Error)是指程序算法的错误,这种错误程序不会发生任何程序中断或跳出程序,而是一直执行到最后,可能有结果。但是执行结果是不对的。 如计算1到10的总数,结果应该为55,利用下面的算法。
运行时错误 运行时错误(Runtime Error)多数发生在不可预期的异常。 比如打开硬盘上的某个文件时,该文件不存在。向硬盘上写某个文件的时候,硬盘的空间不足。 由于网络阻塞得不到预期的数据等等。也有可能是用户不按正确的操作步骤而造成的错误, 如在做除法的时候除数为零,访问数组的时候,超出了可访问下标的范围,这些都是发生错误的原因。比如下面的代码。
程序调试的基本手段 断点 跟踪 监视
断点 断点 断点的属性 清除或禁用断点 用鼠标单击左边 Ctrl+B 调试|窗口|断点 Ctrl+Alt+F9 条件断点(先设置断点,右击,可编辑条件) 命中次数 清除或禁用断点 清除所有断点 Ctrl+Shift+F9
跟踪 单步执行 逐语句 F11 逐过程 F10 点右键,运行到光标处 跳出函数 Shift+F11 拖动到下一句(跳过中间一些语句)
监视 即时监视 快速监视 Ctrl+Alt+Q 自动窗口 调用堆栈 指令 调试|窗口|反汇编
练习 1.练习C#语言中的几个高级特性 2.练习异常的处理 3.练习调试 参见ch04下的示例 参见forDebug下的示例 while循环的跟踪 数组的跟踪(筛法求素数) 构造方法执行过程的跟踪 一个dotNET BUG的跟踪
进一步阅读 电子稿 《C#语言高级特性.doc》 文章 《The Code Project – Exception Handling Best Practices in _NET - _NET.htm》
问题与讨论 dstang2000@263.net