0

I'm stuck with event implementation in C# code. I have class Characters, which contains Name, HP, DMG and DEF. Then I have few random enemies and one hero based on Characters class.

Next I have method, what is picking random attacker and random defender. That means - I have Hero, Dragon, and Wolf. The method will pick random character, let's say the Hero and mark him as "defender". Then it will randomly pick between Dragon and Wolf and mark it as "attacker". And I want to create an event which will be triggered inside this method and it will show which characters are against. I.e. "Hero has chosen the Dragon as his opponent".

I've tried it like

public delegate void ChooseOpponentEventHandler(object source, EventArgs args);
public event ChooseOpponentEventHanlder ChooseEvent;

//Method for choosing random enemy
abstract public bool ChooseAttacker(string Attacker, string Defender);
virtual public bool ChooseOpponent(string Attacker, string Defender, int OppNum, int AttNum)
{
   if(ChooseAttacker(Attacker, Defender) == true && AttNum != OppNum 
   {
      return true;
   }
else return false;
}

protected virtual void OnChooseOpponent()
{
   if (ChooseEvent != null)
   ChooseEvent(this, Event.Args.Empty);
}

I have no idea how to put the method OnChooseOpponent() inside the ChooseOpponent() method and also how to make the Method say

Console.WriteLine(Attacker.Name + "has chosen" + Defender.Name + " as his opponent") 

or something like that, because the Method OnChooseOpponent doesn't know, who Attacker or Defender is.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Tom Benny
  • 73
  • 6

1 Answers1

1

I can already see some compilation errors in your code.

First of all, it's EventArgs, not Event.Args. Then, the Attacker and the Defender are strings. They do not contain a .Name property or field.

As to how to handle your events, you want to notify other classes when the ChooseOpponent is executed, right? What you should do is simply call OnChooseOpoonent(); inside ChooseOpponent.

In order for another class to get notified, they should have an instance of this class, let's say it's named otherClass. What you need to do is:

otherClass.ChooseEvent += EventChosen;

void EventChosen(object sender, EventArgs e)
{
    Console.WriteLine("The event was chosen.");
}

Now, if you want to also pass some additional data with your event, you have to create a separate class that will be transfered along with your event:

public class ChooseEventArgs : EventArgs
{
   public string Attacker {get; set;}
   public string Defender {get; set;}
}

When you fire the event, it should be:

protected virtual void OnChooseOpponent(string attacker, string defender) 
{
    if (ChooseEvent != null)
    ChooseEvent(this, new ChooseEventArgs{Attacker = attacker, Defender = defender});
}

EDIT: as Gserg suggested, the proper way to fire (invoke) an event is with this piece of code instead:

protected virtual void OnChooseOpponent(string attacker, string defender) 
{
    ChooseEvent?.Invoke(this, new ChooseEventArgs{Attacker = attacker, Defender = defender});
}

And in your other class, you can change the signature of the method that will handle the event, like so:

void EventChosen(object sender, ChooseEventArgs e)
{
    Console.WriteLine(e.Attacker + " has chosen " + e.Defender + " as his opponent");
}

I hope that makes sense. To clear things up, you can read this documentation which might explain the concept better than I can.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Sotiris Panopoulos
  • 1,523
  • 1
  • 13
  • 18
  • You still haven't corrected the race condition on `if (ChooseEvent != null)`. – GSerg May 24 '20 at 19:17
  • I'm afraid I don't understand. Why is this a race condition? It's a check to see if there are any subscriptions to this event. – Sotiris Panopoulos May 24 '20 at 19:21
  • Yes, and the result can change after you've checked for != null because some external code may subscribe or unsubscribe right at that moment. See https://stackoverflow.com/a/2123826/11683. Because here we have events in their simplest non-customized form, [this](https://stackoverflow.com/a/44156810/11683) will be enough. You don't need to declare the `Raise` specifically, just `ChooseEvent?.Invoke(this, ...)`. – GSerg May 24 '20 at 19:28
  • Ohh I did not know about the first part, you opened a new world to me! In the first answer, though, it refers to an implicit lock fort the event object, and the problem appears when you want to change the behavior of add and remove methods. – Sotiris Panopoulos May 24 '20 at 19:32
  • Yes, you don't need the `lock` statement when you don't have add/remove, but you still do need to copy the reference to the handler like in the second code block in the answer. The `?.` operator does that. The answer was written before the `?.` operator appeared. – GSerg May 24 '20 at 19:33
  • I agree about the `?.` part. I will edit my answer. Thanks for your insight! – Sotiris Panopoulos May 24 '20 at 19:35
  • I'm sorry to interrupt your conversation, but I don't get the `otherClass` thing. What exactly do you mean by that please? – Tom Benny May 24 '20 at 19:38
  • The whole point of having an event handling mechanism is to notify other classes, correct? Say that this code that we discuss here is in a class named `OtherClass`. Somewhere else in your code, I assume you have an instance of this class and you want to receive "notifications" about this. Let's say you have in MainClass, an instance of `OtherClass`, named `otherClass`. Then you will subscribe on `ChooseEvent` of this object. – Sotiris Panopoulos May 24 '20 at 19:42
  • I have to excuse myself again, but somehow I don't get this. Maybe I'm being just silly and can't read properly, but.. The event is connected to class named `Characters`. When I go to Main in Program class and I try to type `Characters.ChooseEvent`, it isn't working. It is filling only `Characters.ChooseOpponentEventHandler`. – Tom Benny May 24 '20 at 19:51
  • OH I got it. You mean like `var characters = new Characters() {Name, HP, DMG DEF}` ? – Tom Benny May 24 '20 at 20:02
  • Yes. You need an actual instance (object) of the class on which to register for any events, not the class itself. – Sotiris Panopoulos May 24 '20 at 20:03
  • But I got it like `List characters = new List{}` and it doesn't work with the lower `characters`. – Tom Benny May 24 '20 at 20:04
  • Then you need to add the subscription to all the items in the list. – Sotiris Panopoulos May 24 '20 at 20:06
  • So it should go like `characters[0].ChooseEvent += EventChosen;` etc. untill the last item in the list? – Tom Benny May 24 '20 at 20:08
  • Or simply: `foreach(var c in characters) { c.ChooseEvent += EventChosen; }` – Sotiris Panopoulos May 24 '20 at 20:26
  • 1
    @GSerg Race conditions are only relevant if the object is being manipulated from multiple threads at the same time. *Very* few objects need to be designed to act properly when manipulated from multiple threads without the caller synchronizing them. You need a compelling reason *to* make an object safe to be used from multiple threads, not the other way around. I'm not seeing any indication this object actually needs to be designed to be used from multiple threads. – Servy May 24 '20 at 20:26
  • @SotirisKoukios-Panopoulos Yea I made it, but have found out another problem with "No overload for `EventChosen` method matches delegate `Characters.ChooseOpponentEventHandler` " – Tom Benny May 24 '20 at 20:33
  • 1
    `ChooseOpponentEventHandler(object source, EventArgs args)` should also be changed to `ChooseOpponentEventHandler(object source, ChooseEventArgs args)` – Sotiris Panopoulos May 24 '20 at 20:36
  • TRUE! And I swear this is my last thing, but can you check, if the method `ChooseOpponent` is written right? Because I can't call the method `OnChooseOpponent();` inside the `ChooseOpponent`. I've tried every single logical procedure, how to put the method in, but there is always "no given argument" error – Tom Benny May 24 '20 at 20:42
  • What arguments do you pass? Have you changed the signature to accept `attacker` and `defender`? – Sotiris Panopoulos May 24 '20 at 20:50
  • Yes sorry, It works now! I love you for your help! <3 – Tom Benny May 24 '20 at 21:00