4.1 委托(delegate)
大致上—函数指针
委托是对函数原型的包装
委托的声明
public delegate double MyDelegate( doublex)
委托的实例化
MyDelegated d2 =new MyDelegate(obj.myMethod);
委托的调用
委托变量名(参数列表)
d2(8.9)
函数积分的运算
using System;
delegate double Fun(double x );
public class DelegateIntegral
{
public static voidMain()
{
Fun fun = newFun(Math.Sin);
double d = Integral(fun, 0, Math.PI/2, 1e-4 );
Console.WriteLine( d );
Fun fun2 = new Fun(Linear );
double d2 = Integral(fun2, 0, 2, 1e-3 );
Console.WriteLine( d2 );
Rnd rnd = new Rnd();
double d3 = Integral(new Fun(rnd.Num), 0, 1, 0.01 );
Console.WriteLine( d3 );
}
static double Linear(double a )
{
return a*2+1;
}
class Rnd
{
Random r = new Random();
public double Num(double x )
{
return r.NextDouble();
}
}
static doubleIntegral(Fun f, double a, double b, double eps)// 积分计算
{
int n,k;
doublefa,fb,h,t1,p,s,x,t=0;
fa=f(a);
fb=f(b);
// 迭代初值
n=1;
h=b-a;
t1=h*(fa+fb)/2.0;
p=double.MaxValue;
// 迭代计算
while (p>=eps)
{
s=0.0;
for (k=0;k<=n-1;k++)
{
x=a+(k+0.5)*h;
s=s+f(x);
}
t=(t1+h*s)/2.0;
p=Math.Abs(t1-t);
t1=t;
n=n+n;
h=h/2.0;
}
return t;
}
}
委托的合并
多播MultiCastDelegate
一个委托实例中可以“包含”多个函数
调用委托,就是调用其中多个函数
多个函数间的先后顺序是没有意义的
返回值也就没有太多意义
运算符+- += -=
动态地增减其中的函数
提高了程序的灵活性
委托的转换
按声明的名称判断
以下两个不能互相转换或加减
delegate void D(int a)
delegate void B (int a)
委托的相等
按内容(即其中“包含的函数”)来判断
有点点像两个字符串的“相等”与否的判断
委托相当于函数指针
但它类型更安全,是引用类型
且功能更强大,有多播功能
编程实例:函数的绘图txt 委托的多播.txt
4.2事件
事件----回调函数
用户界面中的事件
按钮点击事件
基本的写法
事件的声明
public event 委托名 事件名;
事件的注册与移除
事件名+=或-=
在事件所在类的外面只能用以上两个运算符
事件的发生(激发)
事件名(参数列表)
相当于回调所注册的函数
网络爬虫
EventWhenDownload.cs
定义及使用事件的6步曲
公用的:
声明事件参数类:class xxxEventArgs{}
声明委托:delegate void xxxEventHandler(obj ,args)
在一个类中:
定义时间:public event 类型名称
发生事件:事件名(参数)
在别的类中:
定义一个方法:void方法名(obj,args)
注册时间: xxx.时间+= new 委托(方法名)
事件与委托的关系
事件一定有相关的委托类型
与委托实例一样,事件也“包含”多个函数
事件的运算受更多限制(在类外只能用+=或-=)
事件比委托实例更复杂:
可以定义事件存取器
修饰符 event 委托类型名事件名
{
add{e+=value;}
remove{e-=value;}
}
事件是一种消息机制
事件源调用事件,别的类注册时间
事件的类型是一个委托
4.3Lambda表达式——简洁到极致
泛型(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表达式
相当于匿名方法的简写
省略delegate,甚至省略参数类型
直接用(参数)=>{语句或表达式}
button1.Click+=(sender,e)=>{…}
new Thread (()=>{…}).Start();
PlotFun(x=>x*x,0,100);
匿名函数多一个功能:
不写(参数)的匿名函数
可以转成任意多个参数的委托
Linq:Language Integrated Query
常见的形式
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})
Linq事例
int[] arr= new int[]{8,5,89,3,56,4,1,58};
var m=from n in arr where n<5 orderby nselect n*n;
foreach(var n in m)
{
Console.WriteLine(n);
}
总之
匿名函数使用delegate
Lambda表达式使用=>
Linq使用from,where,select
Lambda及相关的写法.txt
4.4运算符的重载
使用运算符的例子
this.Location+=new Size(10,10);
TimeSpan diff= date2- date1;
String s1,s2;… if (s1==s2)
运算符有时比方法名更直观
如两个负数用 a+b 比 a.Add(b)更直观
但要慎用
运算符重载有一些限制
如成对,如类型要求,如有的不能重载
运算符的声明
一元运算符的声明形式如下
public static 类型 operator 一元运算符(类型 参数名){…}
二院运算符声明的形式如下
public static 类型 operator 二元运算符(类型 参数名,类型 参数名){…}
类型转换运算符声明的形式如下:
public static implicit operator 类型(类型 参数名){…}
public static explicit operator 类型(类型 参数名){…}
复数重载的运算.txt
4.5异常处理
异常的概念
C#中的异常处理
try{…}
catch(Exception e){….}
finally{…}
System.Exception类
public Exception();
public Exception(string);
Message属性
StackTrace属性
几种常用的异常类
System.OutOfMemoryException
System.StackOverflowException
System.NullReferenceException
System.TypeInitializationException
System.InvalidCastException
System.ArrayTypeMismatchException
System.IndexOutOfRangeException
System.MulticastNotSupportedException
System.ArithmeticException
System.DivideByZeroException
System.OverflowException
捕获和处理异常
try{}
catch(AException e1){}
catch(BException e2){}
catch(更一般的Exception e){}
finally{}
注意:catch{}表示捕获所有种类的异常
抛出异常
throw语句抛出一个异常的语法为:
throw expression
一般是这样的
if(xxxxxx) throw new SomeException(信息)
创建用户自定义异常类
从Exception或ApplicationException继承
重抛异常:throws;
异常链接
throw new Exception (“msg”,e);
这里e称为内部异常
InnerException属性
使得外部进一步知道内部的异常原因
算术溢出与checked
对溢出进行检查
对整个程序 csc /checked xxxx.cs
对部分程序
针对表达式:checked(表达式)及unchecked(表达式)
针对快语句:checked{……}及unchecked{….}
对溢出异常进行捕获
try{ }catch( Overflow Exception e){}
4.6Attribute
用在类上的
[Serrializable]
public sealed classString:IComparable,Icloneable,IConvertible,Ienumerable
用在方法上的
[STAThread]
static void Main();
Attribuete是与类、结构、方法等元素相关的额外信息,时候对元信息的扩展。
通过Attribute可以使程序、甚至语言本身的功能得到增强。
[HelpUrl](http://SomeUrl/ApiDocs/SomeClass)]
class SomeClass
{
[WebMethod]
voidGetCustomers(){…}
stringTest([SomeAttr] string param1){…}
}
使用Attribute的一般方式
在程序集、类、域、方法等前面用[]表示
可以省略“Attribute”几个字母,只写xxx
可以带参数
位置参数(相当于构造方法带的参数)
命名参数(域名或属性名=值)
示例
在Main()方法使用[STAThread]
使用“过时”:AttributeObsolete.cs
使用“条件”:AttributeConditional.cs
在结构上、枚举上使用:StructLayout、Flag
在程序集级别应用Attribute
[assembly:AssemblyCopany(“”)]
自定义Attribute
1.声明Attribute类
从System.Attribute继承而来
名字要用xxxAttribute
2.使用Attribute类
在类及成员上面使用方括号
可以省略后缀Attribute
3.通过反射访问属性
4.7C#语言中的其他成分
编译预处理
1.标识符声明
#define定义一个标识符
#undef” 取消定义”一个标识符
2.条件处理
#if,#elif,#else,#endif
3.信息报告
#error和#warning
4.行号标记
#line行号”文件名”
Unfafe及指针
1.Unsafe
用于修饰、方法
2.fixed及指针
fixed(类型 * 指针名= 表达式)语句
3.sizeof运算符
4.stackallc
在栈上分配的内存,而不是在堆上,因此不会担心内存被垃圾回收器自动回收
其他关键字
1.lock
多线程程序中,lock可以将某个对象加锁
2.volatile
随时可能被程序以外的其他因素所修改。域被volatile修饰时,会阻止编译器对他的优化
4.8程序的组织
名字空间程序的逻辑组织
嵌套类型类中嵌套类型
程序集 程序的物理组织
名字空间
1.名字空间的概念
逻辑划分,避免名字冲突
2.名字空间的声明
namespace xxx.xxx{}
3.名字空间的导入
using xxx.xxx
4.使用别名
using 别名= 名字空间或类名
嵌套类型
1.嵌套类型的概念
类型中的类型
class A{public class B{public struct C{}}}
new A.B.C();
2.嵌套类型的可访问性
受各个层次的限制
程序集
模块
程序集
4.9C#语法的小结
类型声明
类型声明是C#程序的主题,他可以位于名字空间中,也可以是嵌套类型。
类型声明包括以下几种:
1)类class
2)结构 struct
3)接口 interface
4)枚举 enum
5)委托delegate
类的成员
1.常数(const)
它代表了与类相关的常数数据。
2.域(field)
它是类中的变量
3.方法(method)
它实现了可以被类实现的计算和行为
4.属性(property)
它定义了命名的属性和与这个属性进行读写相关行为。
5.事件(event)
它定义了由类产生的通知
6.索引(indexer)
它允许类的实例通过与数组相同的方法来索引
7.运算符(operator)
他定义了可以被应用于类的实例上的表达式运算符
8.实例构造函数(instanceconstructor)
它执行需要对类的实例进行初始化的动作
9.析构函数(destructor)
类的实例被清除时实现的动作(结构不能有析构函数)
10.静态构造函数(staticconstructor)
它执行对类本身进行初始化的动作。
11.类型(type)
它代表位于类中的类型