精通C#--委托,事件和Lambda表达式

在.NET平台下,委托类型用来定义和响应应用程序中的回调。
.NET委托类型是一个类型安全的对象,指向可以以后调用的其它方法。【.NET委托支持多路广播和异步方法调用】

#.NET委托类型
##非泛型委托
1.
委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法(或多个方法)。
委托类型包含3个重要的信息:
所调用的方法的名称
方法的参数【可选】
方法的返回值类型【可选】

.NET委托可以指向静态方法,页可以指向实例方法。

一个委托对象被创建并提供上述信息后,可以在运行时动态调用其指向的方法。
.NET Framework中每个委托都被自动赋予同步或异步访问方法的能力。可以直接调用另一个辅助执行线程上的方法。

C#编译器处理委托类型时,先自动产生一个派生自System.MulticastDelegate的密封类。
2.1.Invoke()
以同步方式调用委托对象维护的每个方法
同步Invoke()不能在C#中直接调用
2.2.BeginInvoke()和EndInvoke()
能在第二个执行线程上异步调用当前方法。


// 定义一个委托
// 指向传入两个整数,返回一个整数的方法
public delegate int BinaryOp(int x, int y);

// 这是编译器默认的合成版本
public sealed class BinaryOp : System.MulticastDelegate
{
	public int Invoke(int x, int y);
	public IAsyncResult BeginInvoke(int x, int y, 【委托参数列表】
	AsyncCallback cb, object state);
	public int【委托返回值类型】 EndInvoke(IAsyncResult result);
}

public delegate string MyOtherDelegate(out bool a, ref bool b, int c);

// 编译器默认的合成版本
public sealed class MyOtherDelegate : System.MulticastDelegate
{
	public string【返回值类型】 Invoke(out bool a, ref bool b, int c【参数列表】);
	public IAsyncResult BeginInvoke(out bool a, ref bool b, int c, 【参数列表】
	AsyncCallback cb, object state);
	public string【返回值类型】 EndInvoke(out bool a, ref bool b,【包含out/ref修饰的参数这里也要放上】 IAsyncResult result);
}

// 通用合成类表示
public sealed class DelegateName : System.MulticastDelegate
{
	public delegateReturnValue Invoke(allDelegateInputRefAndOutParams);
	public IAsyncResult BeginInvoke(allDelegateInputRefAndOutParams, AysncCallback cb, object state);
	public delegateReturnValue EndInvoke(allDelegateRefAndOutParams, IAsyncResult result);
}

需要学习的基础设施:
System.MulticastDelegate / System.Delegate

namespace SimpleDelegate
{
	public delegate int BinaryOp(int x, int y);
	public class SimpleMath
	{
		public static int Add(int x, int y)
		{
			return x + y;
		}
		
		public static int Subtract(int x, int y)
		{
			return x - y;
		}
	}

	class Program
	{
		static void Main()
		{
			Console.WriteLine("***Simple Delegate Example***\n");
			BinaryOp b = new BinaryOp(SimpleMath.Add);
			// b(10, 10)等价与 b.Invoke(10, 10)
			Console.WriteLine("10 + 10 is {0}", b(10, 10));
			Console.ReadLine();
		}

		static void DisplayDelegateInfo(Delegate delObj)
		{
			foreach(Delegate d in delObj.GetInvocationList())
			{
				Console.WriteLine("Method Name:{0}", d.Method);
				Console.WriteLine("Type Name:{0}", d.Target);
			}
		}
	}
}

3.使用委托发送对象状态通知–使用委托定义Car
定义将通知发送给调用者的委托类型
声明Car类中每个委托类型的成员变量
在Car上创建辅助函数使调用者能指定由委托成员变量保存的方法
修改Accelerate()方法以在适当情形下调用委托的调用列表

public class Car
{
	public int CurrentSpeed{get; set;}
	public int MaxSpeed{get; set;}
	public string PetName{get; set;}

	private bool carIsDead;
	public Car()
	{
		MaxSpeed = 100;
	}
	public Car(string name, int maxSp, int currSp)
	{
		CurrentSpeed = currSp;
		MaxSpeed = maxSp;
		PetName = name;
	}

	// 1.定义委托类型
	public delegate void CarEngineHandler(string msgForCaller);
	// 2.定义每个委托类型的成员变量
	private CarEngineHandler listOfHandlers;
	// 3.向调用者添加注册函数
	public void RegisterWithCarEngine(CarEngineHandler methodToCall)
	{
		listOfHandlers = methodToCall;
	}

	public void Accelerate(int delta)
	{
		if(carIsDead)
		{
			if(listOfHandlers != null)
			{
				listOfHandlers("Sorry, this car is dead...");
			}
		}
		else
		{
			CurrentSpeed += delta;
			if(10 == (MaxSpeed - CurrentSpeed)
			&& listOfHandlers != null)
			{
				listOfHandlers("Careful buddy! Gonna blow!");
			}

			if(CurrentSpeed >= MaxSpeed)
			{
				CarIsDead = true;
			}
			else
			{
				Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);
			}
		}
	}
}

class Program
{
	static void Main()
	{
		Console.WriteLine("***Delegates as event enablers***\n");
		Car c1 = new Car("SlugBug", 100, 10);
		c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));
		Console.WriteLine("***Speeding up***");
		for(int i = 0; i < 6; i++)
		{
			c1.Accelerate(20);
		}
	
		Console.ReadLine();
	}

	public static void OnCarEngineEvent(string msg)
	{
		Console.WriteLine("\n***Message From Car Object***");
		Console.WriteLine("=>{0}", msg);
		Console.WriteLine("*************\n");
	}
}

4.支持多路广播
一个委托对象可以维护一个可调用方法的列表。
给一个委托对象添加多个方法时,重载+=操作符即可。

public class Car
{
	public void RegisterWithCarEngine(CarEngineHandler methodToCall)
	{
		// 等价的表达
		// if(listOfHandlers == null)
		//    listOfHandlers = methodToCall;
		// else
		//    Delegate.Combine(listOfHandlers, methodToCall);
		listOfHandlers += methodToCall;
	}
	
	...
}

5.从委托的调用列表移除成员

public class Car
{
	public void UnRegisterWithCarEngine(CarEngineHandler methodToCall)
	{
		listOfHandlers -= methodToCall;
	}
}

6.方法组转换语法

static void Main()
{
	Console.WriteLine("***Delegates as event enablers***\n");
	Car c1 = new Car("SlugBug", 100, 10);
	// 常常使用委托对象只是为了传递作为构造函数参数的方法名称
	// 为了简化操作,C#提供了一种叫方法组转换的方法。
	// 允许我们在调用以委托作为参数的方法时直接提供方法名称【创建委托对象并将方法名称作为参数传递给对象构造函数隐式执行】
	c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));
	Car.CarEngineHandler handler2 = new Car.CarEngineHandler(OnCarEngineEvent2);
	c1.RegisterWithCarEngine(handler2);
	...
}

// 
static void Main()
{
	...
	c1.RegisterWithCarEngine(CallMeHere);
	...
}

static void CallMeHere(string msg)
{
	Console.WriteLine("=>Message from Car:{0}", msg);
}

##泛型委托
1.

namespace GenericDelegate
{
	// 泛型委托
	public delegate void MyGenericDelegate<T>(T arg);
	class Program
	{
		static void Main()
		{
			Console.WriteLine("***Generic Delegates***\n");
			MyGenericDelegate<string> strTarget = new MyGenericDelegate<string>(StringTarget);
			strTarget("Some string data");
			MyGenericDelegate<int> intTarget = new MyGenericDelegate<int>(IntTarget);
			intTarget(9);
			Console.ReadLine();
		}

		static void StringTarget(string arg)
		{
			Console.WriteLine("arg in uppercase is:{0}", arg.ToUpper());
		}
	
		static void IntTarget(int arg)
		{
			Console.WriteLine("++arg is:{0}", ++arg);
		}
	}
}

2.泛型Action<>和Func<>委托
使用委托在应用中回调时,须:
自定义一个与要指向的方法的格式相匹配的委托
创建自定义委托的实例,将方法名作为构造函数的参数
通过调用委托对象的Invoke()来间接调用该方法

许多情况下,只需要接受一组参数并返回一个值的委托,委托名无关紧要。
这时可使用Action<>和Func<>。

// Action<>委托的一个目标
static void DisplayMessage(string msg, ConsoleColor txtColor, int printCount)
{
	ConsoleColor previous = Console.ForegroundColor;
	Console.ForegroundColor = txtColor;
	for(int i = 0; i < printCount; i++)
	{
		Console.WriteLine(msg);
	}

	Console.ForegroundColor = previous;
}

static void Main(string[] args)
{
	Console.WriteLine("***Fun with Action and Func***");
	// Action<>委托只能指向返回void的方法。
	// 如果要指向具有返回值的方法【又不不想编写自定义委托】,可以使用Func<>
	Action<string, ConsoleColor, int> actionTarget = new Action<string, ConsoleColor, int>(DisplayMessage);
	actionTarget("Action Message!", ConsoleColor.Yellow, 5);
	Console.ReadLine();
}

///
static int Add(int x, int y)
{
	return x + y;
}

static string SumToString(int x, int y)
{
	return (x+y).ToString();
}

static void Main()
{
	// 最后一个参数固定为调用方法的返回值类型
	Fun<int, int, int> funcTarget = new Func<int, int, int>(Add);
	int result = funcTarget.Invoke(40, 40);
	Console.WriteLine("40+40={0}", result);
	Func<int, int, string> funcTarget2 = new Func<int, int, string>(SumToString);
	string sum = funcTarget2(90, 300);
	Console.WriteLine(sum);
}

#C#事件
为了简化自定义方法的构建来为委托调用列表增加和删除方法,C#提供了event关键字。
编译器处理event关键字时,自动提供注册和注销方法以及必要的委托类型成员变量。这些委托成员变量总是声明为私有的。
event节省了完整的委托定义。
1.定义一个事件
定义一个委托类型,它包含在事件触发时将要调用的方法。
通过C# event关键字用相关委托声明这个事件。

public class Car
{
	// 这个委托用来与Car的事件协作
	public delegate void CarEngineHandler(string msg);

	// 这种汽车可以发送这些事件
	public event CarEngineHandler Exploded;
	public event CarEngineHandler AboutToBlow;
	...
}

public void Accelerate(int delta)
{
	if(carIsDead)
	{
		if(Exploded != null)
		{
			Exploded("Sorry, this car is dead...");
		}
	}
	else
	{
		CurrentSpeed += delta;
		if(10 == MaxSpeed - CurrentSpeed
		&& AboutToBlow != null)
		{
			AboutToBlow("Careful buddy! Gonna blow!");
		}

		if(CurrentSpeed >= MaxSpeed)
		{
			carIsDead = true;
		}
		else
		{
			Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);
		}
	}
}

向调用者发送一个事件,就如通过名称和相关联委托定义的必需参数来指定事件这么简单。

C#事件会扩展为两个隐藏的公共方法。一个带add_前缀,另一个带remove_前缀。

3.监听传入的事件

// NameOfObject.NameOfEvent += new RelatedDelegate(functionToCall);
Car.CarEngineHandler d = new Car.CarEngineHandler(CarExploded);
myCar.Exploded += d;

// NameOfObject.NameOfEvent -= new RelatedDelegate(functionToCall);
myCar.Exploded -= d;

class Program
{
	static void Main()
	{
		Console.WriteLine("***Fun With Events***\n");
		Car c1 = new Car("SlugBug", 100, 10);

		c1.AboutToBlow += new Car.CarEngineHandler(CarIsAlmostDoomed);
		c1.AboutToBlow += new Car.CarEngineHandler(CarAboutToBlow);

		Car.CarEngineHandler d = new Car.CarEngineHandler(CarExploded);
		c1.Exploded += d;
		Console.WriteLine("***Speeding up***");
		for(int i = 0; i < 6; i++)
		{
			c1.Accelerate(20);
		}

		c1.Exploded -= d;
		Console.WriteLine("\n***Speeding up***");
		for(int i = 0; i < 6; i++)
		{
			c1.Accelerate(20);
		}
		Console.ReadLine();
	}
}

public static void CarAboutToBlow(string msg)
{
	Console.WriteLine(msg);
}

public static void CarIsAlmostDoomed(string msg)
{
	Console.WriteLine("=>Critical Message from Car:{0}", msg);
}

public static void CarExploded(string msg)
{
	Console.WriteLine(msg);
}

// 进一步简化事件注册,可以使用方法组转换【在需要传递委托对象的时候,直接传递方法名】
static void Main()
{
	Car c1 = new Car("SlugBug", 100, 10);
	c1.AboutToBlow += CarIsAlmostDoomed;
	c1.AboutToBlow += CarAboutToBlow;
	c1.Exploded += CarExploded;

	....
}

4.创建自定义的事件参数

public class EventArgs
{
	public static readonly EventArgs Empty;
	public EventArgs();
}

public class CarEventArgs : EventArgs
{
	public readonly string msg;
	public CarEventArgs(string message)
	{
		msg = message;
	}
}

public class Car
{
	public delegate void CarEngineHandler(object sender, CarEventArgs e);
	public void Accelerate(int delta)
	{
		if(carIsDead)
		{
			if(Exploded != null)
			{
				Exploded(this, new CarEventArgs("Sorry, this car is dead..."));
			}
		}
		...
	}

	public static void CarAboutToBllow(object sender, CarEventArgs e)
	{
		Console.WriteLine("{0} says:{1}", sender, e.msg);
	}
	...
}

5.泛型EventHandler委托

// T就是自定义的EventArgs类型
// EventHandler<T>
public class Car
{
	// 不再需要一个自定义的委托类型
	// 仅仅适用于 接受object作为参数1,接受EventArgs派生类型作为参数2,返回类型为void的委托
	public event EventHandler<CarEventArgs> Exploded;
	public event EventHandler<CarEventArgs> AboutToBlow;
	...
}

static void Main()
{
	Console.WriteLine("***Prim and Proper Events***\n");
	Car c1 = new Car("SlugBug", 100, 10);
	c1.AboutToBlow += CarIsAlmostDoomed;
	c1.AboutToBlow += CarAboutToBlow;

	EventHandler<CarEventArgs> d = new EventHandler<CarEventArgs>(CarExploded);
	c1.Exploded += d;
	...
}

#C#匿名方法
1.
可以在事件注册时,直接将一个委托与一段代码关联。
这种代码称为匿名方法。
用委托对象的地方,可以用匿名方法,也可以用Lambda表达式。

class Program
{
	static void Main()
	{
		Console.WriteLine("***Anonymous Methods***\n");
		Car c1 = new Car("SlugBug", 100, 10);
		// 注册事件处理程序作为匿名方法
		c1.AboutToBlow += delegate
		{
			Console.WriteLine("Eek! Going too fast!");
		};

		c1.AboutToBlow += delegate(object sender, CarEventArgs e)
		{
			Console.WriteLine("Message from Car:{0}", e.msg);
		};

		c1.Exploded += delegate(object sender, CarEventArgs e)
		{
			Console.WriteLine("Fatal Message from Car:{0}", e.msg);
		};

		for(int i = 0; i < 6; i++)
		{
			c1.Accelerate(20);
		}
		Console.ReadLine();
	}
}

匿名方法内可以访问匿名方法所在作用域内的本地变量。
这些变量称为匿名方法的外部变量。
匿名方法不能访问定义方法中的ref或out参数
匿名方法中的本地变量不能与外部方法中的本地变量重名
匿名方法可以访问外部类作用域的实例变量或静态变量。
匿名方法内的本地变量可以与外部类的成员变量同名。

#Lambda表达式
1.
Lambda表达式只是用更简单的方式来写匿名方法。

public List<T> FindAll(Predicate<T> match);
public delegate bool Predicate<T>(T obj);

class Program
{
	static void Main()
	{
		Console.WriteLine("***Fun With Lambdas***\n");
		TraditionalDelegateSyntax();
		Console.ReadLine();
	}

	static void TraditionalDelegateSyntax()
	{
		List<int> list = new List<int>();
		list.AddRange(new int[]{20, 1, 4, 8, 9, 44});
		// 匿名方法版本
		//List<int> evenNumbers = list.FindAll(
		//delegate(int i)
		//{
		//	return (i%2)==0;
		//});
		// Lambda表达式版本
		// 编译器依据Lambda表达式上下文和底层委托,推断i为int
		// List<int> evenNumbers = list.FindAll(i =>(i%2)==0);
		// 或List<int> evenNumbers = list.FindAll((int i) =>(i%2)==0);
		Predicate<int> callback = new Predicate<int>(IsEvenNumber);
		List<int> evenNumbers = list.FindAll(callback);
		...
	}

	static bool IsEvenNumber(int i)
	{
		return (i % 2) == 0;
	}
}

Lambda表达式可以应用于任何匿名方法或强类型委托的场合。

2.Lambda表达式形式
ArgumentsToProcess => StatementsToProcessThem
Lambda表达式的参数可以显式和隐式类型化。
参数隐式类型化时,参数类型推断得出。返回值类型由表达式结果推断。

static void LambdaExpressionSyntax()
{
	List<int> list = new List<int>();
	list.AddRange(new int[]{20, 1, 4, 8, 9, 44});
	List<int> evenNumbers = list.FindAll(
	(i)=>
	{
		Console.WriteLine("value of i is currently:{0}", i);
		bool isEven = ((i%2)==0);
		return isEven;
	});
}

3.含多个或零个参数的Lambda表达式

public class SimpleMath
{
	public delegate void MathMessage(string msg, int result);
	private MathMessage mmDelegate;
	public SetMathHandler(MathMessage target)
	{
		mmDelegate = target;
	}
}

class Program
{
	static void Main()
	{
		SimpleMath m = new SimpleMath();
		m.SetMathHandler(
		// 无参版本()
		// 显式版本(string msg, int result)
		(msg, result)=>
		{
			Console.WriteLine("Message:{0}, Result:{1}", msg, result);
		});

		m.Add(10, 10);
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值