How to make event calling holder class function - c#

This may have been asked several times, but I don't know what to search for..
Anyway. I have a class called Character. Inside of it I want to have a collision component that I have called RectangleCollision. Inside of it there is a function called IsOverlapping that checks for overlap.
I want to have a function that can be modified for each game object. For example create a function called OnBeginOverlap(); that will fire everytime the collision component detects a collision.
Is there any way that I can bind this function as delegate or event? Or something?

You have to read about events and delegates. There are plenty of examples on the web. The easiest I managed to find when I was trying to understand the subject was this:
The Simplest C# Events Example Imaginable
You can also check out the below (you can compile this as console application):
class Character
{
public delegate void OverlappingHandler(Character character, EventArgs e);
public event OverlappingHandler OverlappingEvent;
public void IsOverlapping()
{
bool overlapping = true;
if (overlapping)
{
if (OverlappingEvent != null)
{
OverlappingEvent(this, null);
}
}
}
}
class Program
{
static void Main(string[] args)
{
Character c = new Character();
c.OverlappingEvent += OverlappingEventHandler;
c.OverlappingEvent += OverlappingSecondEventHandler;
c.IsOverlapping();
Console.Read();
}
static void OverlappingEventHandler(Character character, EventArgs e)
{
Console.WriteLine("We have overlapping here!!");
}
static void OverlappingSecondEventHandler(Character character, EventArgs e)
{
Console.WriteLine("Seriously, we have overlapping !!");
}
}
So step by step:
Create a delegate, which is a bridge between your event and the code you want to run when event is triggered. You give parameters to a delegate, which are (object sender, EventArgs e) - in this example sender is the Character class, arguments are used to send additional info - for example type of character.
Create event of our delegate type
In our function IsOverlapping() there would be your logic checking if there is overlapping happening. If there is, you fire up event. You should check first if there is anything connected to the event (hence the if (OverlappingEvent != null)) - if some there is something, fire up the event.
In the Main() you create an instance of the class and...
Subscribe your event handlers to it, so the code that should be executed when the event is triggered. I connected two methods, just to show that you can subscribe more than one.
Now when you run c.IsOverlapping() this is what happens:
your logic to check overlapping runs,
if there is overlapping, there will be a check if OverlappingEvent has code subscribed (it does in Main()),
if it does event will be triggered,
code subscribed to the event runs - in this case your code in Main().
You can compile this as console app and it will display 2 lines:
We have overlapping here!!
Seriously, we have overlapping !!
Hope this helps.

Related

Raising event inside event

I am trying to get an event to trigger once the code resulting from the trigger of another event finished working. To me, this means that I have to trigger the second event just at the end of said code.
The first event, here, is directly taken from the Vimba Camera API I'm using, and it works perfectly fine. I read a few things on how to call an event properly (particularly the first anwer here), but I can't get my second event to run properly. I probably misunderstood something, but what ?
Here is a pseudo-code version of what I wrote :
public partial class Form1 : Form
{
public delegate void SecondEventHandler(int[] myData);
public event SecondEventHandler SomethingHappened;
//Doing a lot of things irrelevant here
myObj.OnFirstEvent += new Obj.OnFirstEventHandler(this.OnFirstEvent);
private void OnFirstEvent(Data data)
{
//Doing things, and preparing myData
SomethingHappened += new SecondEventHandler(HandleSomethingHappened);
}
void HandleSomethingHappened(int[] myData)
{
//Working with myData
}
}
If you want to raise methods attached to second event:
private void OnFirstEvent(Data data)
{
//Doing things, and preparing myData
var h = SomethingHappened;
if(h != null)
h(pass your int[] parameter)
}
Actually, the easiest yet the cleanest way to achive this is called continuation-passing-style. OnFirstEvent(Data data) should become OnFirstEvent(Data data, Action<int[]> continuator). Whenever it is not null, your code calls it.
However I do warn you: don't let it grow and spread all over your code. It's hard to debug and maintain from a long-term perspective. If you'd expect such an approach to be used extensively, then take a look in reactive extensions.

EventHandler not being called despite InvocationList containing the subscribing method C#

I am developing a MEF application. I am using a plugin as a publisher and another as a subscriber. For the current issue I guarantee that both plugin instances are active. On the subscriber I subscribe to the event and on the publisher I iterate over the invocation list and call the BeginInvoke to raise the event asynchronously as so:
Publisher:
public class BackchannelEventArgs : EventArgs {
public string Intensity { get; }
public BackchannelEventArgs(string intensity) {
this.Intensity = intensity;
}
}
public class Publisher {
public event EventHandler<BackchannelEventArgs> BackchannelEvent = delegate { };
private void BackchannelEventAux(string bcintensity) {
Plugin.LogDebug("BACKCHANNEL EVENT, sending to " + BackchannelEvent.GetInvocationList().Length + " subscribers: " + bcintensity);
var args = new BackchannelEventArgs(bcintensity);
foreach (EventHandler<BackchannelEventArgs> receiver in BackchannelEvent.GetInvocationList()) {
receiver.BeginInvoke(this, args, null, null);
}
}
}
Subscriber (relevant snippet, the Init is being called by a pluginsManager in which I can see the logs):
class Subscriber {
public void Init(){
LogInfo("Before subscribing");
publisher.BackchannelEvent += HandleBackchannelEvent;
LogInfo("After subscribing");
}
private void HandleBackchannelEvent(object sender, BackchannelEventArgs e) {
LogDebug("Handle Backchannel!");
}
}
Now, the Log you see on the event handler is not called at all. I have 4 other events that follow the same structure and somewhat this event in particular is not being called (I can see the logs on the other events). The other plugins follow the exact same structure.
Already tried:
Call synchronously BackchannelEvent(this, args) but the results are the same;
Subscribe this same event on the other plugins as well but the issue remains on this single event (and not on the others who follow the same structure).
I hope you can give me some help on this.
Thank you
Edit: The shown code is a snippet. The Init method is being called by the pluginsManager. I have put a log before the subscribing call and I can confirm that I am indeed subscribing.
Edit2: The number of elements in the InvocationList is in fact 2 (the empty delegate and the subscriber) so it checks out.
Okay. I don't know why but I figured out the solution so that other ones who stumble with the issue can find a solution. It was related with a extension I created for the Random class (which wasn't throwing a exception although... so it might be a bug on C#, I can't really explain). The Random extension is provided by an external NuGet package I created.
Version A (without using the Random Extension):
Body of the EventHandler:
LogDebug("Inside Handler");
double intensityValue2 = GetRandomNumber(Settings.MinimumIntensity, Settings.MaximumIntensity);
double frequency2 = GetRandomNumber(Settings.MinimumFrequency, Settings.MaximumFrequency);
int repetitions2 = GetRandomInt(Settings.MinimuMRepetitions, Settings.MaximumRepetitions);
Version B (using Random extension):
Body of EventHandler:
LogDebug("Inside Handler");
double intensityValue2 = random.GetRandomNumber(Settings.MinimumIntensity, Settings.MaximumIntensity);
double frequency2 = random.GetRandomNumber(Settings.MinimumFrequency, Settings.MaximumFrequency);
int repetitions2 = random.GetRandomNumber(Settings.MinimuMRepetitions, Settings.MaximumRepetitions);
Version A is the one that it is working. The Logs are guaranteed to appear. I don't know why it isn't letting me use extensions but it is solved for now. It would make sense if the Random extension threw an exception but it is not the case...
If any other person stumbles upon the issue I hope this helps you figure out the issue faster than me.
Thank you
Edit: typo

Event Driven Programming

I've been reading this MSDN article and this question to try to understand events in .NET. Unfortunately, its not clicking for me and I'm having a lot of trouble. I'm trying to integrate this technique into my project, with little success.
Basically, I've got this class that will read numbers. Whenever it encounters a new number, I want it to fire an event called numberChanged.
So, I set up my event public event EventHandler numberChanged;. Later on, I fire my event when it encounters a number than isn't the same as the previous one.
if(currentNumber != previousNumber){
if(numberChanged != null){
numberChanged(this, new EventArgs());
}
}
But then I'm having trouble 'subscibing' to this event. If I do numberChanged += [something to do here] it errors saying that numberChanged is an event and not a type.
Is my explanation clear enough for some advice to be offered? Many thanks.
There are a number of ways to handle it, the most basic is to create a function:
public void MyNumberChangedHandler(object sender, EventArgs e)
{
//Your code goes here that gets called when the number changes
}
You then subscribe (one time only, usually in the constructor) by going:
numberChanged += MyNumberChangedHandler;
Or, you can use something called an anonymous (lambda) method, which is also assigned in your constructor (typically):
numberChanged += (sender, e) => {
//Your code here to handle the number changed event
};
To expand a little bit, care must be taken when using the lambda approach since you can create memory leaks and zombie objects. The .NET memory garbage collector is a mark-and-sweep system that removes objects when they are no longer in use. This post shows how hard it is to remove lambda event handlers: How to remove a lambda event handler .
Having an active event handler can keep your object alive even if it has been disposed! Here is an example of creating a zombie object (doesn't run in Fiddle but you can copy to your own console app) https://dotnetfiddle.net/EfNpZ5
Prints out:
I'm still alive
I'm still alive
I was disposed!
Press any key to quit
I'm still alive
I'm still alive
I'm still alive.
As everything else in the C# programming world, the events concept also follows specific rules and has it's own syntax. The wording is as follows:
an event defined as EventHandler is actually just a shortcut for a special method (delegate) signature - public delegate void EventHandler(object sender, EventArgs e)[1]. Whenever you have a signature in C# you always know what you need to write on the right sight or as a parameter, in order to connect/call some objects/methods/and so on.
after the event is defined, you need to subscribe in order to be informed whenever something happens. The syntax for subscribing an event is +=. Naturally for unsubscribing is -=. MSDN says that the syntax should be object.event += eventHandler (or object.event += new EventHandler(eventHandler);)
so after an event is defined (event Event SomeEvent;) all that left is to create a method that can be bound to this event. This method has to have the same signature as the EventHandler, so it should match the signature of [1] and can be something like private void numberChangedEventHandler(object sender, EventArgs eventArguments)
Now you know what you need to write on the right side of +=.
An example:
public class NumberSequence
{
// numbers to be compared
private readonly List<int> numbers = new List<int>();
// used to generate a random collection
private readonly Random random = new Random();
// tell me if the previous and next number are different
public event EventHandler DifferentNumbersEvent;
public NumberSequence()
{
// fill the list with random numbers
Enumerable.Range(1, 100).ToList().ForEach(number =>
{
numbers.Add(random.Next(1, 100));
});
}
public List<int> Numbers { get { return numbers; } }
public void TraverseList()
{
for (var i = 1; i < this.numbers.Count; i++)
{
if (this.numbers[i - 1] != this.numbers[i])
{
if (this.DifferentNumbersEvent != null)
{
// whoever listens - inform him
this.DifferentNumbersEvent(this, EventArgs.Empty);
}
}
}
}
}
Now before the class is used, define the event handler, that will listen and will be called, when the event is fired (wording again):
private void differentNumberEventHandler(Object sender, EventArgs eventArguments)
{
Console.WriteLine("Different numbers...");
}
And the usage:
var ns = new NumberSequence();
ns.DifferentNumbersEvent += differentNumberEventHandler;
ns.TraverseList();
Everything else is just syntactic sugar for this notation (lambda / anonymous methods / ...), for example:
object.Event += (s, e) => { // code ... }; is the same as object.Event += (Object sender, EventArgs eventArguments) => { // code ... };. Do you recognise the signature? - it is the same as the private void differentNumberEventHandler....
Often we need to pass information through the event, in this case maybe we want to see the two numbers. C# allows you to do this easily using custom event arguments. Just create a class that inherits the EventArgs class and add properties for the data that should be passed, in this case the numbers:
public class NumbersInfoEventArgs : EventArgs
{
public int Number1 { get; set; }
public int Number2 { get; set; }
}
And then specify, when declaring the event, that it will pass data of type NumbersInfoEventArgs (signatures again):
public event EventHandler<NumbersInfoEventArgs> DifferentNumbersEvent;
...
this.DifferentNumbersEvent(this, new NumbersInfoEventArgs
{
Number1 = this.numbers[i - 1],
Number2 = this.numbers[i]
});
And last but now least, the signature of the event handler should match the signature of the event:
private void differentNumberEventHandler(Object sender, NumbersInfoEventArgs eventArguments)
{
Console.WriteLine("Different numbers {0} - {1}", eventArguments.Number1, eventArguments.Number2);
}
And voila, the output is:
Different numbers 89 - 86
Different numbers 86 - 53
Different numbers 53 - 12
Different numbers 12 - 69
you can subscribe the event in this way:
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var num = new Number();
num.numberChanged +=(s,e) =>{
Console.WriteLine("Value was changed to {0}",num.Value); // in the demo below you can find another implementation for this sample using custom events
};
num.Value=10;
num.Value=100;
}
}
public class Number{
public event EventHandler numberChanged;
private int _value=0;
public int Value
{
get{
return _value;
}
set{
if(value!=_value){
_value=value;
if(numberChanged!=null)
numberChanged(this,null);
}
}
}
}
explanation:
since the EventHandler delegate has 2 parameters (sender, eventArgs) as mentioned here, you need to pass these params and I passed them as s and e
another way to subscribe this event like this:
var num = new Number();
num.numberChanged += NumberChanged_Event; // below is the delegate method
public void NumberChanged_Event(object sender,EventArgs e)
{
// your code goes here
}
I updated the demo to work with you own delegate to pass the old value and new value which can help in many cases.
here a working demo

Trouble with raising custom event handling between 2 forms

New to C#. Like the title, I'm having difficulty trying to raise an event. It will eventually then be consumed on another form.
What I'm trying to do is have many instances of a custom user control (my event raising form(s)) that creates a tcp client, connects, and then closes. When this tcp client has an "error", be it a catch exception, I want an event to be raised. I'm forcing the error right now by having my internet turned off to test. My first problem is I can't even get the event to be raised at all. I'll show the event code I'm working with on my custom user control:
public delegate void TaskCompleteEventHandler(object sender, TaskCompleteEventArgs e);
public event TaskCompleteEventHandler TaskComplete;
public class TaskCompleteEventArgs : System.EventArgs
{
// add local member variables to hold text
private string errorString;
// class constructor
public TaskCompleteEventArgs(string ErrorString)
{
this.errorString = ErrorString;
}
// Property
public string ErrorString
{
get
{
return errorString;
}
set
{
errorString = value;
}
}
}
This is my method that processes the exception and ideally would raise the event and allow the host form to print the string and exception accordingly.
private void ErrorLogging(string ex)
{
errorString = String.Format(/*...errorString formatting...*/);
// instance the event args and pass it the errorString value
TaskCompleteEventArgs args = new TaskCompleteEventArgs(errorString);
// raise the event with the updated arguments
TaskComplete(this, args); //----> THIS IS WHERE I GET AN ERROR!! <----
this.Dispose();
}
The error is Object reference not set to an instance of an object.
Here's the Watch screen of my TaskComplete(this, args)
I can't seem to debug this... I'm just not strong enough yet to know what I've done wrong. How is it causing side effects?
I'm sure I'm going to have more issues on my main form when I get this going... Does anyone have a clue what's causing this? Thanks in advance.
EDIT: On my main form:
public Form1()
{
InitializeComponent();
// Start control disabled and subscribe each control the event
foreach (var control in controlList)
{
control.Enabled = false;
control.TaskComplete += new dev_emu_project.dev_emu_widget.TaskCompleteEventHandler(OnTaskComplete);
}
}
List<dev_emu_project.dev_emu_widget> controlList = new List<dev_emu_project.dev_emu_widget>();
public void OnTaskComplete(object sender, dev_emu_project.TaskCompleteEventArgs e)
{
//.... work for processing
}
}
You are getting a NullReferenceException because you're invoking an empty event, meaning no delegate has been registered to it. You need to make sure TaskComplete isn't null before invoking it.
Add a null check before invoking to make sure someone did register to your event:
if (TaskComplete != null)
{
TaskComplete(this, args);
}
From MSDN Event Tutorial:
Invoking an event
Once a class has declared an event, it can treat that event just like a field of the indicated delegate type. The field will either be null, if no client has hooked up a delegate to the event, or else it refers to a delegate that should be called when the event is invoked. Thus, invoking an event is generally done by first checking for null and then calling the event

C#: What's the intended replacement for firing events from outside an object?

C# has gone to great lengths to ensure that you cannot "fire" an event, e.g.:
form.FormClosed(this, new FormClosedEventArgs(CloseReason.UserClosing));
button.Click(this, new EventArgs());
customer.AddressChanged(this, new EventArgs());
don't compile because you cannot fire an event this way.
This seems to have been a conscious decision on the part of the language designers. They seem to be intentionally trying to prevent "bad behavior".
i am trying to find the intended replacement.
Practical Example:
void ShowPopup(Form form)
{
ToolStripDropDown toolDrop = new ToolStripDropDown();
ToolStripControlHost toolHost = new ToolStripControlHost(form);
toolHost.Margin = new Padding(0);
toolDrop.Padding = new Padding(0);
toolDrop.Items.Add(toolHost);
toolDrop.Closed += toolDrop_Closed;
toolDrop.Show(screenLocation);
}
void toolDrop_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
//The form's FormClosed event doesn't fire when shown as a popup
//Fire the event manually
form.FormClosed(this, new FormClosedEventArgs(CloseReason.UserClosing));
}
If i'm not supposed to fire events from outside the object - what is the technique intended to replace it?
If there is no intended replacement for firing events, is there any article, book, or Channel 9 video that explains how i should handle this situation?
An event inside a class has often a public method used by class clients to invoke the event from outside of the class:
public class Foo {
public event FooDelegate FooEvent;
public void RaiseFoo() {
if ( FooEvent != null ) FooEvent();
}
}
If an event is missing such public "trigger", it usually means that for some reason you should not be able to raise the event from outside.
In your example, the event can of course be raised with
form.Close();
with, apart from other things, also raises the event.

Categories

Resources