委托与实践
分类索引: C# 语言和运行时剖析--前言
元旦休假了一段时间,所以没有更新,现在开始恢复更新,一周一到两章。
之前的章节在知识点的组织结构上更多参考了《CLR Via C#》这本书。从这章开始,打算脱离这本书的章节结构,来完全讲一些自己的总结。不过在代码实例上还是会引用这本书的案例。
委托的概念
一.定义:委托是一种封装方法的类型, 通过委托可以调用方法,相当于C/C++中函数指针的概念. 但不同的是,委托不光是一个函数地址, 而是一个面向对象的类型. 其中封装了指向对象的实例和方法。
二.要点:委托相对于其他类型来说相对抽象,因为其中有很多特殊的封装。
使用委托时,不能用 delegate 来定义一个实例,而必须先使用 delegate 来定义一个新的类型(真正使用的委托类型),这个类型提供了委托所需要指向的方法的模板.然后用这个自定义类型再申明实例使用。
委托的实例不仅封装了一个方法的引用,而且可以形成委托链,对聚合的多个方法进行链式调用。
FCL提供了多个已封装的常用委托类型,如 Action , Func , EventHandler 等,善于利用这些已封装的委托类型,特别是泛型委托,可以实现更好的设计。
委托虽然是封装方法调用的类型,但他可以作为其他方法的参数以及局部变量被调用,这也是一个较难理解的知识点。
委托有多种调用方式,在.NET 3.0之后的版本,可以使用Lambda表达式来编码较为简单的委托封装。
委托可以封装其他类型的实例方法,也可以封装静态方法。
三.代码示例
internal sealed class DelegateIntro {
// 申明一个委托类型;此类型的实例指向一个采用Int32参数,以及返回void的方法
internal delegate void Feedback(Int32 value);
public static void Go() {
StaticDelegateDemo();
InstanceDelegateDemo();
ChainDelegateDemo1( new DelegateIntro());
ChainDelegateDemo2( new DelegateIntro());
}
/// <summary>
/// 静态方法封装
/// </summary>
private static void StaticDelegateDemo() {
Console.WriteLine( " ----- Static Delegate Demo ----- " );
Counter( 1 , 3 , null );
Counter( 1 , 3 , new Feedback(DelegateIntro.FeedbackToConsole));
Counter( 1 , 3 , new Feedback(FeedbackToMsgBox)); // "Program." is optional
Console.WriteLine();
}
/// <summary>
/// 实例方法封装
/// </summary>
private static void InstanceDelegateDemo() {
Console.WriteLine( " ----- Instance Delegate Demo ----- " );
DelegateIntro di = new DelegateIntro();
Counter( 1 , 3 , new Feedback(di.FeedbackToFile));
Console.WriteLine();
}
/// <summary>
/// 委托链实例一
/// </summary>
/// <param name="di"></param>
private static void ChainDelegateDemo1(DelegateIntro di) {
Console.WriteLine( " ----- Chain Delegate Demo 1 ----- " );
Feedback fb1 = new Feedback(FeedbackToConsole);
Feedback fb2 = new Feedback(FeedbackToMsgBox);
Feedback fb3 = new Feedback(di.FeedbackToFile);
Feedback fbChain = null ;
fbChain = (Feedback)Delegate.Combine(fbChain, fb1);
fbChain = (Feedback)Delegate.Combine(fbChain, fb2);
fbChain = (Feedback)Delegate.Combine(fbChain, fb3);
Counter( 1 , 2 , fbChain);
Console.WriteLine();
fbChain = (Feedback)Delegate.Remove(fbChain, new Feedback(FeedbackToMsgBox));
Counter( 1 , 2 , fbChain);
}
/// <summary>
/// 委托链示例2
/// </summary>
/// <param name="di"></param>
private static void ChainDelegateDemo2(DelegateIntro di) {
Console.WriteLine( " ----- Chain Delegate Demo 2 ----- " );
Feedback fb1 = new Feedback(FeedbackToConsole);
Feedback fb2 = new Feedback(FeedbackToMsgBox);
Feedback fb3 = new Feedback(di.FeedbackToFile);
Feedback fbChain = null ;
fbChain += fb1;
fbChain += fb2;
fbChain += fb3;
Counter( 1 , 2 , fbChain);
Console.WriteLine();
fbChain -= new Feedback(FeedbackToMsgBox);
Counter( 1 , 2 , fbChain);
}
private static void Counter(Int32 from , Int32 to, Feedback fb) {
for (Int32 val = from ; val <= to; val++ ) {
// If any callbacks are specified, call them
if (fb != null )
fb(val);
}
}
private static void FeedbackToConsole(Int32 value) {
Console.WriteLine( " Item= " + value);
}
private static void FeedbackToMsgBox(Int32 value) {
MessageBox.Show( " Item= " + value);
}
private void FeedbackToFile(Int32 value) {
StreamWriter sw = new StreamWriter( " Status " , true );
sw.WriteLine( " Item= " + value);
sw.Close();
}
}
委托的使用
一.委托的一般调用
使用场景:如上一节中的代码示例可作为委托链的一般调用案例。
使用顺序:
定义委托类型。
定义委托实例。
封装引用方法。
通过委托实例来调用封装方法。
二.委托链的一般调用
使用场景:如上一节中的代码示例可作为委托链的一般调用案例。
使用方法:
定义委托类型。
定义委托实例。
封装多个引用方法或者从委托链中删除方法引用。
通过委托实例来实现链式调用方法。
使用GetInvocationList()获取委托链的信息,示例如下:
nternal static class GetInvocationList {
// Define a Light component.
internal sealed class Light {
// This method returns the light's status.
public String SwitchPosition() {
return " The light is off " ;
}
}
// Define a Fan component.
internal sealed class Fan {
// This method returns the fan's status.
public String Speed() {
throw new InvalidOperationException( " The fan broke due to overheating " );
}
}
// Define a Speaker component.
internal sealed class Speaker {
// This method returns the speaker's status.
public String Volume() {
return " The volume is loud " ;
}
}
// Definition of delegate that allows querying a component's status.
private delegate String GetStatus();
public static void Go() {
// Declare an empty delegate chain.
GetStatus getStatus = null ;
// Construct the three components, and add their status methods
// to the delegate chain.
getStatus += new GetStatus( new Light().SwitchPosition);
getStatus += new GetStatus( new Fan().Speed);
getStatus += new GetStatus( new Speaker().Volume);
// Show consolidated status report reflecting
// the condition of the three components.
Console.WriteLine(GetComponentStatusReport(getStatus));
}
// Method that queries several components and returns a status report
private static String GetComponentStatusReport(GetStatus status) {
// If the chain is empty, there is nothing to do.
if (status == null ) return null ;
// Use this to build the status report.
StringBuilder report = new StringBuilder();
// Get an array where each element is a delegate from the chain.
// 此处status是一个GetStatus委托的实例,封装了一个委托链
Delegate[] arrayOfDelegates = status.GetInvocationList();
// Iterate over each delegate in the array.
foreach (GetStatus getStatus in arrayOfDelegates) {
try {
// Get a component's status string, and append it to the report.
report.AppendFormat( " {0}{1}{1} " , getStatus(), Environment.NewLine);
}
catch (InvalidOperationException e) {
// Generate an error entry in the report for this component.
Object component = getStatus.Target;
report.AppendFormat(
" Failed to get status from {1}{2}{0} Error: {3}{0}{0} " ,
Environment.NewLine,
((component == null ) ? "" : component.GetType() + " . " ),
getStatus.Method.Name, e.Message);
}
}
// Return the consolidated report to the caller.
return report.ToString();
}
}
三.异步调用委托
通过delegate封装的委托类型所派生的实例会自动生成Invoke(), BeginInvoke(), EndInvoke()方法,由此可见可以异步的调用委托。
异步调用委托的使用顺序是:
定义委托类型。
定义委托实例。
封装委托方法。
使用BeginInvoke()异步调用委托方法,并指定一个 AsyncCallback 委托类型的实例方法,例如Callback(),作为异步操作的回调操作方法。
在Callback()中根据BeginInvoke()执行的结果(作为 IAsyncResult 类型的参数传入),在Callback()中调用EndInvoke()方法。
实现异步调用委托的同时,需要跟其他系统委托类型打交道,也增加了理解难度,请注意。
接下来,我们省略定义委托类型这一步,使用一个 Action 委托(可以理解为一个自定义委托类型)来演示异步调用委托。
class Program
{
static void Main( string [] args)
{
// Action委托的异步委托
Action< int > action = Speak;
AsyncCallback callback = new AsyncCallback(CallBackMethod);
action.BeginInvoke( 10 , callback, action);
Console.WriteLine( " 当前线程ID:{0} " , Thread.CurrentThread.ManagedThreadId.ToString());
Console.ReadKey();
}
// 回调方法
static void CallBackMethod(IAsyncResult ar)
{
Action < int > a = (Action< int > )ar.AsyncState;
a.EndInvoke(ar);
}
// 被委托方法
static void Speak( int i)
{
string str = string .Format( " The number is {0} " ,i);
Console.WriteLine( " 当前线程ID:{0} " ,Thread.CurrentThread.ManagedThreadId.ToString());
Thread.Sleep(TimeSpan.FromSeconds( 3 ));
Console.WriteLine(str);
}
}
委托实践
一.在实践中常用的FCL中已定义的委托类型
EventHandler :FCL中各种前端控件中的事件,基本都用这个委托类型封装。我们在编码中的自定义事件,如果没有特殊的传递需求( EventArgs ),也可以直接使用这个委托类型。
Action <T>:此委托类型适用于各种不需要返回数据的方法类型封装,支持泛型,最多支持十六个输入参数 。
Func <in T1, out T2):此委托类型适用于各种需要返回数据的方法类型封装,但不支持带有 ref , out 修饰符参数的方法封装。
各种CallBack回调函数封装:例如 AsyncCallback,Wait Callback 等。
其他封装:例如用于查询的 Predicate <T>, 用于转换的 Converter <T1,T2>, 用于比较的 Comparison <T>等。
二.使用Lambda表达式
Lambda表达式可以理解成为是一种对匿名方法的封装,这个主要在后面的进阶特性中讲。
因为委托是对方法和方法组的封装,所以委托封装的对象自然可以用Lambda表达式来编写,这个在一些使用委托作为参数的方法中常用。
列举几个数组Array中的方法和线程池中的异步方法调用作为示例:
internal static class AnonymousMethods {
public static void Go() {
// Create and initialize a String array
String[] names = { " Jeff " , " Kristin " , " Aidan " };
// Get just the names that have a lowercase 'i' in them.
Char charToFind = ' i ' ;
names = Array.FindAll(names, delegate (String name) { return (name.IndexOf(charToFind) >= 0 ); });
// Convert each string's characters to uppercase
names = Array.ConvertAll<String, String>(names, delegate (String name) { return name.ToUpper(); });
// Sort the names
Array.Sort(names, delegate (String name1, String name2) { return String.Compare(name1, name2);});
Array.Sort(names, (x1, x2) => { x1 = " 1 " ; return string .Compare(x1, x2); });
// Display the results
Array.ForEach(names, delegate (String name) { Console.WriteLine(name); });
}
private sealed class AClass {
private static void CallbackWithoutNewingADelegateObject() {
ThreadPool.QueueUserWorkItem( delegate (Object obj) { Console.WriteLine(obj); }, 5 );
}
}
private sealed class AClass2 {
private static void UsingLocalVariablesInTheCallbackCode(Int32 numToDo) {
// Some local variables
Int32[] squares = new Int32[numToDo];
AutoResetEvent done = new AutoResetEvent( false );
// Do a bunch of tasks on other threads
for (Int32 n = 0 ; n < squares.Length; n++ ) {
ThreadPool.QueueUserWorkItem(
delegate (Object obj) {
Int32 num = (Int32)obj;
// This task would normally more time consuming
squares[num] = num * num;
// If last task, let main thread continue running
if (Interlocked.Decrement( ref numToDo) == 0 )
done.Set();
}, n);
}
// Wait for all the other threads to finish
done.WaitOne();
// Show the results
for (Int32 n = 0 ; n < squares.Length; n++ )
Console.WriteLine( " Index {0}, Square={1} " , n, squares[n]);
}
}
}
分类: NET01-C# 语言和运行时剖析
标签: C# , CLR
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did47216
元旦休假了一段时间,所以没有更新,现在开始恢复更新,一周一到两章。
之前的章节在知识点的组织结构上更多参考了《CLR Via C#》这本书。从这章开始,打算脱离这本书的章节结构,来完全讲一些自己的总结。不过在代码实例上还是会引用这本书的案例。
委托的概念
一.定义:委托是一种封装方法的类型, 通过委托可以调用方法,相当于C/C++中函数指针的概念. 但不同的是,委托不光是一个函数地址, 而是一个面向对象的类型. 其中封装了指向对象的实例和方法。
二.要点:委托相对于其他类型来说相对抽象,因为其中有很多特殊的封装。
使用委托时,不能用 delegate 来定义一个实例,而必须先使用 delegate 来定义一个新的类型(真正使用的委托类型),这个类型提供了委托所需要指向的方法的模板.然后用这个自定义类型再申明实例使用。 委托的实例不仅封装了一个方法的引用,而且可以形成委托链,对聚合的多个方法进行链式调用。 FCL提供了多个已封装的常用委托类型,如 Action , Func , EventHandler 等,善于利用这些已封装的委托类型,特别是泛型委托,可以实现更好的设计。 委托虽然是封装方法调用的类型,但他可以作为其他方法的参数以及局部变量被调用,这也是一个较难理解的知识点。 委托有多种调用方式,在.NET 3.0之后的版本,可以使用Lambda表达式来编码较为简单的委托封装。 委托可以封装其他类型的实例方法,也可以封装静态方法。三.代码示例
internal sealed class DelegateIntro { // 申明一个委托类型;此类型的实例指向一个采用Int32参数,以及返回void的方法 internal delegate void Feedback(Int32 value); public static void Go() { StaticDelegateDemo(); InstanceDelegateDemo(); ChainDelegateDemo1( new DelegateIntro()); ChainDelegateDemo2( new DelegateIntro()); } /// <summary> /// 静态方法封装 /// </summary> private static void StaticDelegateDemo() { Console.WriteLine( " ----- Static Delegate Demo ----- " ); Counter( 1 , 3 , null ); Counter( 1 , 3 , new Feedback(DelegateIntro.FeedbackToConsole)); Counter( 1 , 3 , new Feedback(FeedbackToMsgBox)); // "Program." is optional Console.WriteLine(); } /// <summary> /// 实例方法封装 /// </summary> private static void InstanceDelegateDemo() { Console.WriteLine( " ----- Instance Delegate Demo ----- " ); DelegateIntro di = new DelegateIntro(); Counter( 1 , 3 , new Feedback(di.FeedbackToFile)); Console.WriteLine(); } /// <summary> /// 委托链实例一 /// </summary> /// <param name="di"></param> private static void ChainDelegateDemo1(DelegateIntro di) { Console.WriteLine( " ----- Chain Delegate Demo 1 ----- " ); Feedback fb1 = new Feedback(FeedbackToConsole); Feedback fb2 = new Feedback(FeedbackToMsgBox); Feedback fb3 = new Feedback(di.FeedbackToFile); Feedback fbChain = null ; fbChain = (Feedback)Delegate.Combine(fbChain, fb1); fbChain = (Feedback)Delegate.Combine(fbChain, fb2); fbChain = (Feedback)Delegate.Combine(fbChain, fb3); Counter( 1 , 2 , fbChain); Console.WriteLine(); fbChain = (Feedback)Delegate.Remove(fbChain, new Feedback(FeedbackToMsgBox)); Counter( 1 , 2 , fbChain); } /// <summary> /// 委托链示例2 /// </summary> /// <param name="di"></param> private static void ChainDelegateDemo2(DelegateIntro di) { Console.WriteLine( " ----- Chain Delegate Demo 2 ----- " ); Feedback fb1 = new Feedback(FeedbackToConsole); Feedback fb2 = new Feedback(FeedbackToMsgBox); Feedback fb3 = new Feedback(di.FeedbackToFile); Feedback fbChain = null ; fbChain += fb1; fbChain += fb2; fbChain += fb3; Counter( 1 , 2 , fbChain); Console.WriteLine(); fbChain -= new Feedback(FeedbackToMsgBox); Counter( 1 , 2 , fbChain); } private static void Counter(Int32 from , Int32 to, Feedback fb) { for (Int32 val = from ; val <= to; val++ ) { // If any callbacks are specified, call them if (fb != null ) fb(val); } } private static void FeedbackToConsole(Int32 value) { Console.WriteLine( " Item= " + value); } private static void FeedbackToMsgBox(Int32 value) { MessageBox.Show( " Item= " + value); } private void FeedbackToFile(Int32 value) { StreamWriter sw = new StreamWriter( " Status " , true ); sw.WriteLine( " Item= " + value); sw.Close(); } }
委托的使用
一.委托的一般调用
使用场景:如上一节中的代码示例可作为委托链的一般调用案例。 使用顺序: 定义委托类型。 定义委托实例。 封装引用方法。 通过委托实例来调用封装方法。二.委托链的一般调用
使用场景:如上一节中的代码示例可作为委托链的一般调用案例。 使用方法: 定义委托类型。 定义委托实例。 封装多个引用方法或者从委托链中删除方法引用。 通过委托实例来实现链式调用方法。 使用GetInvocationList()获取委托链的信息,示例如下:
nternal static class GetInvocationList { // Define a Light component. internal sealed class Light { // This method returns the light's status. public String SwitchPosition() { return " The light is off " ; } } // Define a Fan component. internal sealed class Fan { // This method returns the fan's status. public String Speed() { throw new InvalidOperationException( " The fan broke due to overheating " ); } } // Define a Speaker component. internal sealed class Speaker { // This method returns the speaker's status. public String Volume() { return " The volume is loud " ; } } // Definition of delegate that allows querying a component's status. private delegate String GetStatus(); public static void Go() { // Declare an empty delegate chain. GetStatus getStatus = null ; // Construct the three components, and add their status methods // to the delegate chain. getStatus += new GetStatus( new Light().SwitchPosition); getStatus += new GetStatus( new Fan().Speed); getStatus += new GetStatus( new Speaker().Volume); // Show consolidated status report reflecting // the condition of the three components. Console.WriteLine(GetComponentStatusReport(getStatus)); } // Method that queries several components and returns a status report private static String GetComponentStatusReport(GetStatus status) { // If the chain is empty, there is nothing to do. if (status == null ) return null ; // Use this to build the status report. StringBuilder report = new StringBuilder(); // Get an array where each element is a delegate from the chain. // 此处status是一个GetStatus委托的实例,封装了一个委托链 Delegate[] arrayOfDelegates = status.GetInvocationList(); // Iterate over each delegate in the array. foreach (GetStatus getStatus in arrayOfDelegates) { try { // Get a component's status string, and append it to the report. report.AppendFormat( " {0}{1}{1} " , getStatus(), Environment.NewLine); } catch (InvalidOperationException e) { // Generate an error entry in the report for this component. Object component = getStatus.Target; report.AppendFormat( " Failed to get status from {1}{2}{0} Error: {3}{0}{0} " , Environment.NewLine, ((component == null ) ? "" : component.GetType() + " . " ), getStatus.Method.Name, e.Message); } } // Return the consolidated report to the caller. return report.ToString(); } }
三.异步调用委托
通过delegate封装的委托类型所派生的实例会自动生成Invoke(), BeginInvoke(), EndInvoke()方法,由此可见可以异步的调用委托。 异步调用委托的使用顺序是: 定义委托类型。 定义委托实例。 封装委托方法。 使用BeginInvoke()异步调用委托方法,并指定一个 AsyncCallback 委托类型的实例方法,例如Callback(),作为异步操作的回调操作方法。 在Callback()中根据BeginInvoke()执行的结果(作为 IAsyncResult 类型的参数传入),在Callback()中调用EndInvoke()方法。 实现异步调用委托的同时,需要跟其他系统委托类型打交道,也增加了理解难度,请注意。 接下来,我们省略定义委托类型这一步,使用一个 Action 委托(可以理解为一个自定义委托类型)来演示异步调用委托。
class Program { static void Main( string [] args) { // Action委托的异步委托 Action< int > action = Speak; AsyncCallback callback = new AsyncCallback(CallBackMethod); action.BeginInvoke( 10 , callback, action); Console.WriteLine( " 当前线程ID:{0} " , Thread.CurrentThread.ManagedThreadId.ToString()); Console.ReadKey(); } // 回调方法 static void CallBackMethod(IAsyncResult ar) { Action < int > a = (Action< int > )ar.AsyncState; a.EndInvoke(ar); } // 被委托方法 static void Speak( int i) { string str = string .Format( " The number is {0} " ,i); Console.WriteLine( " 当前线程ID:{0} " ,Thread.CurrentThread.ManagedThreadId.ToString()); Thread.Sleep(TimeSpan.FromSeconds( 3 )); Console.WriteLine(str); } }
委托实践
一.在实践中常用的FCL中已定义的委托类型
EventHandler :FCL中各种前端控件中的事件,基本都用这个委托类型封装。我们在编码中的自定义事件,如果没有特殊的传递需求( EventArgs ),也可以直接使用这个委托类型。 Action <T>:此委托类型适用于各种不需要返回数据的方法类型封装,支持泛型,最多支持十六个输入参数 。 Func <in T1, out T2):此委托类型适用于各种需要返回数据的方法类型封装,但不支持带有 ref , out 修饰符参数的方法封装。 各种CallBack回调函数封装:例如 AsyncCallback,Wait Callback 等。 其他封装:例如用于查询的 Predicate <T>, 用于转换的 Converter <T1,T2>, 用于比较的 Comparison <T>等。二.使用Lambda表达式
Lambda表达式可以理解成为是一种对匿名方法的封装,这个主要在后面的进阶特性中讲。 因为委托是对方法和方法组的封装,所以委托封装的对象自然可以用Lambda表达式来编写,这个在一些使用委托作为参数的方法中常用。 列举几个数组Array中的方法和线程池中的异步方法调用作为示例:
internal static class AnonymousMethods { public static void Go() { // Create and initialize a String array String[] names = { " Jeff " , " Kristin " , " Aidan " }; // Get just the names that have a lowercase 'i' in them. Char charToFind = ' i ' ; names = Array.FindAll(names, delegate (String name) { return (name.IndexOf(charToFind) >= 0 ); }); // Convert each string's characters to uppercase names = Array.ConvertAll<String, String>(names, delegate (String name) { return name.ToUpper(); }); // Sort the names Array.Sort(names, delegate (String name1, String name2) { return String.Compare(name1, name2);}); Array.Sort(names, (x1, x2) => { x1 = " 1 " ; return string .Compare(x1, x2); }); // Display the results Array.ForEach(names, delegate (String name) { Console.WriteLine(name); }); } private sealed class AClass { private static void CallbackWithoutNewingADelegateObject() { ThreadPool.QueueUserWorkItem( delegate (Object obj) { Console.WriteLine(obj); }, 5 ); } } private sealed class AClass2 { private static void UsingLocalVariablesInTheCallbackCode(Int32 numToDo) { // Some local variables Int32[] squares = new Int32[numToDo]; AutoResetEvent done = new AutoResetEvent( false ); // Do a bunch of tasks on other threads for (Int32 n = 0 ; n < squares.Length; n++ ) { ThreadPool.QueueUserWorkItem( delegate (Object obj) { Int32 num = (Int32)obj; // This task would normally more time consuming squares[num] = num * num; // If last task, let main thread continue running if (Interlocked.Decrement( ref numToDo) == 0 ) done.Set(); }, n); } // Wait for all the other threads to finish done.WaitOne(); // Show the results for (Int32 n = 0 ; n < squares.Length; n++ ) Console.WriteLine( " Index {0}, Square={1} " , n, squares[n]); } } }
分类: NET01-C# 语言和运行时剖析
标签: C# , CLR
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did47216