C#中的委托 类似于 C/C++函数的指针
委托:表示引用某个方法的引用类型变量,运行时可以更改引用对象
委托可以用于处理事件和回调函数,所有委托类都是从System.Delegate类继承而来的
需要定义能够被委托所引用的方法,任意委托可以引用与该委托拥有相同签名的方法
public delegate int MyDelegate(string s);
//可以引用任何一个以字符为参数的方法,且返回值类型为整型
//声明委托的句法规则
delegate
声明委托之后,必须使用new关键字和一个特定的方法来创建一个委托对象。创建时,传递到new语句的参数写法与方法调用相同,但是不带有参数
using System;
delegate int NumberChanger(int n);
namespace Consol_App
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
//创建委托实例
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
//使用委托对象调用方法
nc1(25);
Console.WriteLine("Value of Num:{0}", getNum());
nc2(5);
Console.WriteLine("Value of Num:{0}", getNum());
Console.ReadKey();
}
}
}
委托的多播
委托对象可通过“+”运算符进行合并。一个合并委托可以调用它所合并的两个委托,但只有相同类型的委托可以被合并。
“-”运算符则可用于从合并的委托移除一个委托。
using System;
delegate int NumberChanger(int n);
namespace Consol_App
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
//创建委托实例
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1;
nc += nc2;
//调用多播
nc(5); //顺序执行
Console.WriteLine("Value of Num:{0}", getNum()); //75
Console.ReadKey();
}
}
}
下面的实例演示了委托的作用,示例中的printString委托可用于引用带有一个字符串作为输入的方法, 且不返回数据吧
using System;
using System.IO;
namespace Consol_App
{
class PrintString
{
static FileStream fs;
static StreamWriter sw;
//委托声明
public delegate void printString(string s);
//该方法打印到控制台
public static void WriteToScreen(string str)
{
Console.WriteLine("The String is: {0}", str);
}
public static void WriteToFile(string s)
{
fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
sw.WriteLine(s);
sw.Flush();
sw.Close();
fs.Close();
}
//该方法把委托作为参数,并使用它调用方法
public static void sendString(printString ps)
{
ps("hello world");
}
static void Main(string[] args)
{
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
sendString(ps1);
sendString(ps2);
Console.ReadKey();
}
}
}
事件指一个用户操作:按键、点击、移动鼠标,也可以是系统生成的通知。
当事件发生时,应用需要对其做出相应 的反应,如中断。
另外,事件也用于内部进程通信
通过事件使用委托
事件生成于类的声明中,通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件,称为发布器类
其他接受该事件的类称为订阅器类。事件使用的是发布-订阅模型。
发布器是一个定义了事件和委托的对象,此外还定义了事件和委托之间的关联。一个发布器类的对象调用这个事件,同时通知其他的对象。
订阅器是一个接受事件并提供事件处理程序的对象。在发布器类中的委托调用订阅器类中的方法
在类中声明一个事件,首先需要声明该事件对应的委托类型
public delegate void BoilerLogHandler(string status)
使用event关键字声明这个事件
public event BoilerLogHangler BoilerEventLog;
该事件生成时会自动调用一个委托
using System;
namespace Consol_App
{
using System;
public class EventTest
{
private int value;
public delegate void NumMainpulationHandler();
public event NumMainpulationHandler ChangeNum;
protected virtual void OnNumChanged()
{
if (ChangeNum != null)
{
ChangeNum();
}
else
{
Console.WriteLine("Event fired!");
}
}
public EventTest(int n)
{
SetValue(n);
}
public void SetValue(int n)
{
if (value != n)
{
value = n;
OnNumChanged();
}
}
}
public class MainClass
{
public static void Main()
{
EventTest e = new EventTest(5);
e.SetValue(7);
e.SetValue(0);
e.SetValue(11);
Console.ReadKey();
}
}
}
该实例为一个简单的应用程序,该程序用于热水锅炉系统故障排除。当维修工程师检查锅炉时,锅炉的温度、压力以及工程师所写 的备注都会自动记录到一个日志文件中
using System;
using System.IO;
namespace Consol_App
{
class Boiler
{
private int temp;
private int pressure;
public Boiler(int t, int p)
{
temp = t;
pressure = p;
}
public int getTemp()
{
return temp;
}
public int getPressure()
{
return pressure;
}
}
//事件发布器
class DelegateBoilerEvent
{
public delegate void BoilerLogHandler(string status);
//基于上述委托定于的事件
public event BoilerLogHandler BoilerEventLog;
public void LogProgcess() //检验数据
{
string remarks = "o . k";
Boiler b = new Boiler(100, 12);
int t = b.getTemp();
int p = b.getPressure();
if (t > 150 || t < 100 || p < 12 || p > 15)
{
remarks = "Need Maintenance";
}
OnBoilerEventLog("Logging Info:\n");
OnBoilerEventLog("Temparature" + t + "\nPressure:" + p);
OnBoilerEventLog("\nMessage:" + remarks);
}
protected void OnBoilerEventLog(string message)
{
if (BoilerEventLog != null)
{
BoilerEventLog(message);
}
}
}
//该类保留写入日志文件的条款
class BoilerInfoLogger
{
FileStream fs;
StreamWriter sw;
public BoilerInfoLogger(string filename)
{
fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
}
public void Logger(string info)
{
sw.WriteLine(info);
}
public void close()
{
sw.Close();
fs.Close();
}
}
//事件订阅器
public class RecordBoilerInfo
{
static void Logger(string info)
{
Console.WriteLine(info);
}
static void Main(string[] args)
{
BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt"); //文件的对象 c创建文件
DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent(); //事件发布器的实例
boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(Logger); //委托
boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(filelog.Logger);
boilerEvent.LogProgcess();
Console.ReadLine();
filelog.close();
}
}
}
在运行时传递程序中各种元素(类、方法、结构、枚举、组件)的行为信息的声明性标签。可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
特性用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息
.Net框架提供了两种类型的特征:预定义特性和自定义特性
列举特性
[attribute(positional_parameters,name_parameter = value ,...)]element
.Net Framework提供了三种预定义的特性:
该特性描述了用户定义的特性类是如何使用的,它规定了某个特性应用的项目类型
[AttributeUsage(validon,AlolowMultiplr = allowmultiple,Inherited = inherited)]
//validon:承载特性的语言元素
//allowmultiple:如果为true,该特性是多用的。默认值是false(单用的)
//inherited:如果为true,该特性可被派生类继承。默认值是false(不可继承)
这个预定义特性标记了一个条件方法,其执行依赖于特定的预处理标识符
它会引起方法调用的条件编译,取决于指定的值,比如Debug或Trace。例如,当调试代码时显示变量的值
[Conditional(conditionalSymbol)]
[Conditional("DEBUG")]
using System;
using System.Diagnostics;
class MyClass
{
[Conditional("DEBUG")]
//条件编译,可以图通过Conditional指定函数和属性是否编译到最终产品中去
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}
class Test
{
static void function1()
{
MyClass.Message("In Function 1.");
function2();
}
static void function2()
{
MyClass.Message("In Function 2.");
}
public static void Main()
{
MyClass.Message("In Main function.");
function1();
Console.ReadKey();
}
}
标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素
例如:当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为obsolete(过时的)
[Obsolete(message)]
[Obsolete(message.iserror)]
//message 字符串,描述项目为什么过时的原因以及该替换使用什么
//iserror 布尔值,true:编译器应把该项目的使用当作一个错误
//false(编译器生成一个警告)
using System;
public class MyClass
{
[Obsolete("Don't use OldMethod,use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod();
}
}
编译器会报错
.Net允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关
创建并适应自定义特性包含四个步骤:
//为整形类型声明一个叫Age的属性
public int Age
{
get
{
return age;
}
set
{
age = value;
}
}
抽象类可拥有抽象属性,这些属性应在派生类中被实现
element-type this[int index]
{
//get访问器
get
{
//返回index指定的值
}
//set访问器
set
{
//设置index指定的值
}
}
泛型允许编写一个可以与任何数据类型协作的类或方法
pubilc class MyGenericArray
{
public T[] array;
}
static void Swap(ref T lhs,ref T rhs)
{
t temp;
temp = lhs;
rhs = temp;
}
//调用Swap进行交换
Swap(ref a, ref b);
Swap(ref c, ref d);
delegate T NumberChanger(T n)
将一段代码块作为委托参数的技术
//使用匿名方法创建委托实例
delegate void NumberChanger(int n);
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method:{0}",x);//匿名方法的主体
}
//使用匿名方法调用委托
nc(10);
unsafe:使用了指针变量的代码----不安全代码
使用了unsafe修饰符时指针的使用
static unsafe void Main(string[] args)
{
int var = 20;
int* p = &var;
Console.WriteLine("Data id:{0}",var);
Console.WriteLine("Address is:{0}",(int)p);
Console.ReadKey();
}
//输出
Data is:20
Address is: 99215364
编译不安全的代码
必须切换命令行编译器到指定的/unsafe命令行才能进行不安全代码的编译
例如,编译一个包含不安全代码的名为prog1.cs的程序,需要在命令行中输入如下命令
csc /unsafe prog1.cs