我正在努力了解代表和事件,到目前为止我知道这些概念。
我有一个问题,想知道我是不对。
有一类汽车。我们创建一个公共委托(CarHandler),然后我们创建委托类型的私有成员(ListofMethods),然后我们创建用这个成员注册方法的方法(RegistorMethods),我们说ListofMethods + =传入参数。
然后,在主程序中,我们创建具有与委托相同签名的方法(签名是返回类型void,参数是字符串)。然后,我们创建Car类的对象。然后我们用delegate注册方法/方法(方法是console.writeline(incomming parameter))。然后当我们调用这个类时。现在,根据类中调用ListofMethods的位置(例如:ListofMethods(“Hey There”);),相应地注册RegistoredMethods。
因此,使用事件代替以上示例的优点是: 我知道我们可以创建多个具有相同委托类型的事件,而无需创建更多的注册方法。
案例1仅使用委托而不使用任何事件。案例2正在使用事件。然后,在案例1中,所有已注册的方法将获得与ListofHandler调用的相同的文本。要在案例1中创建更多事件(这里的事件意味着一般英语含义而不是c#事件),我们需要创建更多委托成员,使用此委托成员注册新方法的更多方法。但是,在事件的情况下(案例2),不同的事件可以提供自己的文本,然后实例可以注册它所需的事件,并且它会被解雇。
在CASE 1中,我们需要创建更多的委托成员来提升多个事件(而不是C#事件,一般英语意思),在CASE 2(事件)的情况下,仅创建1个委托成员就足够了。是吗?
问题: 以上是正确的方法来实现CASE 3,就像案例2,但只使用委托而不是事件。请你在答案中写下这方面的说明
如果不理解那么你可以问我问题。请帮我在这里表达我的怀疑。
CASE 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); }
}
答案 0 :(得分:1)
两种情况之间的区别基本上归结为这种差异:
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...");
可以在Exploded
支票和通话之间删除null
上的代理人。临时分配可以防止该问题。
答案 1 :(得分:1)
你的两个案件几乎相同。唯一的不同之处在于,当您在类中使用event
(即“案例2”)并且未明确实现它时,编译器会自动生成您必须在“案例1“,以及允许订阅/注册的方法。
经常让人惊喜的事情,即使偶尔会使用C#一段时间的人,也是如上所述的声明:
并且您没有明确地实现它
这句话是什么意思?许多人没有意识到,就像使用属性一样,可以让编译器实现成员,也可以自己完成。
如果是属性,则实施get
和/或set
方法。对于事件,方法名为add
和remove
。当然,如果您自己实现它,您还需要提供支持字段或其他机制来跟踪订阅者(就像在属性中一样)。
那么,这在你的具体例子中意味着什么呢?好吧,对我来说,这意味着如果你有类似事件的语义,那么你肯定应该继续将其作为一个真正的event
成员来实现。无论你采用哪种方式,代码都将基本编译为等效的IL,但使用event
可以利用语言的高级抽象。这使得代码更易于读写,因此使其更易于维护,并且不太可能包含错误。
您可以记住“案例1”中的方法,以防您在声明event
不起作用的情况下结束(例如某种与平台或API无法互操作的情况)处理或支持.NET event
范例。但在大多数情况下,event
是可行的方法。
您关注的部分似乎是delegate
成员的问题(即声明的delegate
类型)。坦率地说,无论您采用哪种方式处理问题,都会遇到此问题。如果您有一种方法可以为类中的多个event
成员重用单个委托类型,那么您还可以将该单个委托类型重用于显式字段和注册方法方法(“案例1”)。
在大多数情况下,您不应该声明自己的委托类型。只需使用例如EventHandler<T>
,或其中一种通用Action
或Func
类型。