[英]Understanding the event usage wrt delegates and registration methods
我试图了解代表和事件,到目前为止,我已经知道了概念。
我心中有一个问题,想知道我是否正确。
有一个班车。 我们创建一个公共委托(CarHandler),然后创建一个委托类型的私有成员(ListofMethods),然后在此方法中创建用于向该成员注册方法的方法(RegistorMethods),即ListofMethods + = incoming参数。
然后,在主程序中,我们创建签名与委托签名相同的方法(签名为返回类型void,参数为字符串)。 然后,我们创建Car类的对象。 然后我们向委托注册方法/方法(方法为console.writeline(incomming参数))。 然后,当我们调用此类时。 现在,基于在类中调用ListofMethods的位置(示例:ListofMethods(“ Hey There”);),将相应地触发RegistoredMethods。
因此,使用事件而不是上面的示例的优点是:我知道我们可以创建多个相同委托类型的事件,而无需创建更多的注册方法。
情况1仅使用委托,没有事件。 情况2使用事件。 然后,在情况1中,所有注册的方法将获得与ListofHandler调用的文本相同的文本。 要在案例1中创建更多事件(这里的事件表示通用英语含义,而不是c#事件),我们将需要创建更多的委托成员,并需要更多的方法向该委托成员注册新方法。 但是,在事件(案例2)的情况下,不同的事件可以给出自己的文本,然后实例可以注册所需的事件并将其触发。
在案例1中,我们将需要创建更多的委托成员来引发多个事件(不是C#事件,一般意义上的英语),而在案例2(事件)的情况下,仅创建1个委托成员就足够了。 那正确吗?
问题:以上最正确的方式来实现CASE 3,与情况2类似,但仅使用委托而不是事件。 请您在答案中写一个便条
如果不了解,您可以问我问题。 请在这里帮助我清除我的疑问。
案例1的代码:
public class Car
{
// 1) Define a delegate type.
public delegate void CarEngineHandler(string msgForCaller);
// 2) Define a member variable of this delegate.
//this can be public, and if public then we can avoid writing the below RegisterWithCarEngine method, but it is not safe
//because user can mess the values and call custom strings, etc
private CarEngineHandler listOfHandlers;
// 3) Add registration function for the caller.
public void RegisterWithCarEngine(CarEngineHandler methodToCall)
{
//listOfHandlers = methodToCall;
listOfHandlers += methodToCall;
}
// Internal state data.
public int CurrentSpeed { get; set; }
public int MaxSpeed { get; set; }
public string PetName { get; set; }
// Is the car alive or dead?
private bool carIsDead;
// Class constructors.
public Car()
{
MaxSpeed = 100;
}
public Car(string name, int maxSp, int currSp)
{
CurrentSpeed = currSp;
MaxSpeed = maxSp;
PetName = name;
}
// 4) Implement the Accelerate() method to invoke the delegate's
// invocation list under the correct circumstances.
public void Accelerate(int delta)
{
// If this car is "dead," send dead message.
if (carIsDead)
{
if (listOfHandlers != null)
listOfHandlers("Sorry, this car is dead...");
}
else
{
CurrentSpeed += delta;
// Is this car "almost dead"?
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(string[] args)
{
Console.WriteLine("***** Delegates as event enablers *****\n");
// First, make a Car object.
Car c1 = new Car("SlugBug", 100, 10);
// Now, tell the car which method to call
// when it wants to send us messages.
c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));
// Speed up (this will trigger the events).
Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);
Console.ReadLine();
Car c2 = new Car("SlugBug1", 100, 10);
// Speed up (this will trigger the events).
Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c2.Accelerate(20);
Console.ReadLine();
}
// This is the target for incoming events.
public static void OnCarEngineEvent(string msg)
{
Console.WriteLine("\n***** Message From Car Object *****");
Console.WriteLine("=> {0}", msg);
Console.WriteLine("***********************************\n");
}
}
案例2的代码:
public class Car
{ // This delegate works in conjunction with the
// Car's events.
public delegate void CarEngineHandler(string msg);
// This car can send these events.
public event CarEngineHandler Exploded;
public event CarEngineHandler AboutToBlow;
...
}
public void Accelerate(int delta)
{
// If the car is dead, fire Exploded event.
if (carIsDead)
{
if (Exploded != null)
Exploded("Sorry, this car is dead...");
}
else
{ CurrentSpeed += delta;
// Almost dead?
if (10 == MaxSpeed - CurrentSpeed && AboutToBlow != null)
{
AboutToBlow("Careful buddy! Gonna blow!");
}
// Still OK!
if (CurrentSpeed >= MaxSpeed)
carIsDead = true;
else
Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);
}
}
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Events *****\n");
Car c1 = new Car("SlugBug", 100, 10);
// Register event handlers.
c1.AboutToBlow += CarIsAlmostDoomed;
c1.AboutToBlow += CarAboutToBlow;
c1.Exploded += CarExploded;
Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
c1.Accelerate(20);
c1.Exploded -= CarExploded;
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); }
}
两种情况之间的区别基本上可以归结为这种区别:
public class Car
{
void RegisterWithCarEngine(CarEngineHandler methodToCall);
}
public class Car
{
event CarEngineHandler Exploded;
event CarEngineHandler AboutToBlow;
}
情况1很奇怪。 没有什么可以让此类的使用者知道此方法的作用-或何时触发。 而且,也许更重要的是,没有方法可以分离事件处理程序。
情况2更标准。 它符合给出良好命名约定的概念,并且很明显这两个成员是事件。 因此,对于消费者来说很明显,他们可以将这些事件附加和分离。
您需要考虑一下以下问题:
public class Car
{
void SetSpeed(string speedName, int speed);
int GetSpeed(string speedName);
}
然后,我可以这样编码:
car.SetSpeed("Max", 50);
car.SetSpeed("Current", 10);
Console.WriteLine(car.GetSpeed("Max"));
Console.WriteLine(car.GetSpeed("Current"));
现在,尽管这名义上提供了与您的类相同的功能-并且可能会争辩说它提供了更多的功能-但它隐藏了该类的使用者所看到的功能。
最好使用案例2提供的接口。
只是附带说明,您应该始终这样调用事件代码:
var x = Exploded;
if (x != null)
x("Sorry, this car is dead...");
可以在null
检查和调用之间删除Exploded
上的委托。 临时分配可避免该问题。
您的两种情况几乎相同。 唯一的实质区别是,当您在类中使用event
(即“情况2”)并且没有显式实现该事件时,编译器会自动生成您必须在“情况1”中声明的字段,以及允许订阅/注册的方法。
像我上面的这样的陈述经常让人感到惊讶,甚至偶尔使那些已经使用C#的人感到惊讶:
而且您没有明确实现它
那句话是什么意思? 许多人没有意识到,对于属性,可以让编译器实现成员,也可以自己实现。
对于该属性,可以实现get
和/或set
方法。 如果发生事件,这些方法分别称为add
和remove
。 当然,如果您自己实现,则还需要提供支持字段或其他机制来跟踪订户(就像在属性中一样)。
那么,这在您的特定示例中意味着什么? 好吧,对我来说,这意味着,如果您具有类似事件的语义,那么您绝对应该继续将其实现为实际的event
成员。 不管您采用哪种方式,代码基本上都会全部编译为等效的IL,但是使用event
可以利用语言的高级抽象。 这使代码更易于读取和编写,并因此更易于维护且更不可能包含错误。
您可以牢记“情况1”中的方法,以防万一您在声明event
不起作用的情况下event
工作(例如,与不处理或不支持的平台或API进行某种互操作)。 NET event
范例)。 但是在大多数情况下, event
才是路要走。
似乎您担心的一部分是delegate
成员的问题(即声明的delegate
类型)。 坦白说,无论您采用哪种方式解决问题,都遇到了这个问题。 如果您有一种方法可以为一个类中的多个event
成员重用单个委托类型,那么您也可以将该单个委托类型用于显式的域和注册方法(“情况1”)。
在大多数情况下,无论如何您都不应该声明自己的委托类型。 只需使用EventHandler<T>
或通用Action
或Func
类型之一即可。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.