c#, .net, delegate, asynchronous callback. What am I doing wrong here? - c#

Heres a little class for a button in a menu in a game.
I'd like to be able to pass a delegate method _triggerMethod when I'm instantiating each button. Then that delegate method will get called when that button instances trigger method is called.
I'm trying out delegates in C# for the first time here. And as far as I'm interpreting the documentation here what I'm doing should work but visual studio is giving me a compile error in the Trigger method.
According to msdn article the code calling the delegate doesnt need to know about the original methods paramaters and such. What am I doing wrong?
Also in the msdn article they are only typing "Del" and that does not work for me. I must type "Delegate" which is odd.
class MenuItem
{
private Rectangle clickArea;
private string displayText;
private Vector2 _position;
private Delegate _triggerMethod;
public MenuItem(Vector2 pos,string txt,Delegate trig)
{
displayText = txt;
_position = pos;
_triggerMethod = trig;
}
public void Draw(SpriteBatch sb)
{
}
public void Select()
{
}
public void DeSelect()
{
}
public void IsMouseOnMe()
{
}
public void Trigger()
{
_triggerMethod();
}
}

You haven't created any delegate definition.
Example (from the MSDN-page you linked):
public delegate void Del(string message);
Then, you need to use that as your Type:
** snip **
private Del _triggerMethod;
public MenuItem(Vector2 pos,string txt,Del trig)
{
displayText = txt;
_position = pos;
_triggerMethod = trig;
}
** snip **
public void Trigger()
{
_triggerMethod("some message");
}
You can pass references of the delegate around without actually knowing what arguments it expects (since it's just a normal reference), but when you want to invoke it, you do need to give it the correct parameters.

You need to define the Del type if you want to use it:
public delegate void Del(string message);
public class MenuItem
{
private Del _triggerMethod;
public void Trigger()
{
_triggerMethod("Message");
}
}
Note that you can use the built-in Action<string> delegate type instead of defining your own:
Action<string> _triggerMethod;
_triggerMethod("Message");
If you just use the Delegate type, you can invoke it using DynamicInvoke:
public void Trigger()
{
_triggerMethod.DynamicInvoke();
}

Related

Godot C# "Error calling method from signal"

Given my scene tree:
Main_control (Type:Control) (Parent)
mnk_game (Type: Control) (Child)
I can run methods in mnk_game's script using signals from Main_control, but it won’t work the other way around (calling a method in Main_control using a signal from mnk_game)
Error:
emit_signal: Error calling method from signal 'mnk_game_finished': 'Control(Main_control.cs)::mnk_show_game': Method not found..
Parent's code:
public class Main_control : Control
{
public override void _Ready()
{pass}
public void mnk_show_game()
{
GD.Print("mnk_show_game reached"); //What I want to reach
}
}
Child code:
public class mnk_game : Control
{
[Signal]
public delegate void mnk_game_finished();
private Godot.Control game_controller;
public override void _Ready()
{
game_controller = (Control)GetParent<Godot.Control>();
this.Connect("mnk_game_finished", game_controller, "mnk_show_game");
}
public void any_method()
{
this.EmitSignal("mnk_game_finished");
}
}
When I call the any_method method at the child's script, I want to be able to reach mnk_show_game method in the parent's script. Can I do this? If so, how?
I don't have much experience using Godot with C#, but I can see that mnk_game_finished was defined without parameters and mnk_show_game also does not take parameters. However, you are emitting the signal with a parameter this.EmitSignal("mnk_game_finished", ds.board). Try to emit the signal without a parameter like so: this.EmitSignal("mnk_game_finished") or define your delegate and function with parameters.

Why Generic method for delegate events not working with error message Method name expected

Work on C#.
My delegate and events are bellow
#region delegate event
#region
public delegate void NotifynextDeviceReceivedDelegate(CustomEventArgs customEventArgs);
public event NotifynextDeviceReceivedDelegate NotifynextDeviceReceivedEvent;
#endregion
#region
public delegate void NotifynextDeviceedDelegate(CustomEventArgs customEventArgs);
public event NotifynextDeviceedDelegate NotifynextDeviceedEvent;
#endregion
#region
public delegate void NotifynextReceiveddDelegate(CustomEventArgs customEventArgs);
public event NotifynextReceiveddDelegate NotifynextReceiveddEvent;
#endregion
#endregion
To invoke i used bellow syntax it's work perfectly
if (NotifynextDeviceReceivedEvent != null)
{
CustomEventArgs customEventArgs = new CustomEventArgs(receivedMessage, receivedTopic);
//Raise Event. All the listeners of this event will get a call.
NotifynextDeviceReceivedEvent(customEventArgs);
}
Need to write same above syntax for all of the event delegate.So,i decided to write generic event to invoke them like bellow:
InvokeEvents<NotifynextDeviceReceivedDelegate>(receivedMessage,receivedTopic,NotifynextDeviceReceivedEvent)
public static InvokeEvents<T>(string receivedMessage,string receivedTopic, T notifynextDeviceReceivedEvent)
{
if (notifynextDeviceReceivedEvent != null)
{
CustomEventArgs customEventArgs = new CustomEventArgs(receivedMessage, receivedTopic);
notifynextDeviceReceivedEvent(customEventArgs);//here is the problem show me error message
}
}
In InvokeEvents method why notifynextDeviceReceivedEvent show me error Method name expected
You could write your code as this:
private static void InvokeEvents<T>(string receivedMessage, string receivedTopic, T eventDelegate)
{
if (eventDelegate != null)
{
var customEventArgs = new CustomEventArgs(receivedMessage, receivedTopic);
((Delegate)(object)eventDelegate).DynamicInvoke(customEventArgs);
}
}
This works. I tested it.
You do require the double cast to make the compiler happy.
There's also no point in making the accessor public as you can't pass a event delegate to it from anywhere outside of the class.
So, since you can't all this code from outside your class, you then must write code within your class to call InvokeEvents, like this:
public void OnNotifynextDeviceedEvent()
{
InvokeEvents("", "", this.NotifynextDeviceedEvent);
}
Which really then means you could have to repeat code in any case.
Now, with C# 6's syntax then you can write these methods as this:
public void OnNotifynextDeviceedEvent()
=> this.NotifynextDeviceedEvent?.Invoke(new CustomEventArgs("", ""));
So you're really not saving much code - none in fact - and you're creating a weakly-typed method. You really should stick with the basic approach.

How to use events in c#?

I am completely new to events in c# and this is what I want to do:
I have two methods:
OpenPage1();
OpenPage2();
When either of these methods is called, I want a third method named as PerformSomeTask() to be called.
I believe that this can be done by event handling. Could anyone tell me how to do this?
All you have to do in your class is to add an event with a correct eventHandler (Action in your example). The eventHandler should correspond the method that will subscribe to this event.
Then you fire the event from the openPage Methods.
You must check for null in case no one subscribed to this event.
public class Foo
{
public event Action theEvent;
public void OpenPage1()
{
if (theEvent != null)
theEvent();
}
public void OpenPage2()
{
if (theEvent != null)
theEvent();
}
}
public class Bar
{
public int Counter { get; set; }
public void PerformSomeTask()
{
Counter++;
}
}
And here's a test that you can run to see it all together:
[TestMethod]
public void TestMethod1()
{
var foo = new Foo();
var bar = new Bar();
foo.theEvent += bar.PerformSomeTask;
foo.OpenPage1();
foo.OpenPage2();
Assert.AreEqual(2, bar.Counter);
}
Events is a big part of C#.
To be simple, you need first a delegate that describe type of called method. In your example, PerformSomeTask is void and take no parameters.
So declare in your class
public delegate void PerformSomeTask();
Then, you need to declare event, which is the member that will called to launch your function
public event PerformSomeTask OnPerformSomeTask;
On your both methods, OpenPage1 and OpenPage2, you need to check if someone subscribe to your event, if yes, call it.
if(OnPerformSomeTask != null)
OnPerformSomeTask();
This will launch every method that subscribe to your event. Subscribers can be multiple.
To subscribe, just do it like this :
YourClass.OnPerformSomeTask += MyVoidMethod;
[...]
public void MyVoidMethod() { DoSomething(); [...] }
Your void method will be called everytime your run OpenPage1 and OpenPage2
If you need some parameters, juste change your delegate to proceed.
public delegate void PerformSomeTask(string myParam);
Then, your methods will have this parameter as standard function parameter (call your event with your value to pass it as parameter to every subscriber function).

How do I pass objects in EventArgs

I have a usercontrol that raises an event after communicating with a web service. The parent handles this event when raised. What I thought would be the proper approach would be to pass the object returned from the webservice to the parent as eventargs???
If this is the proper way I can't seem to find the instructions on how to do so.
UserControl
public event EventHandler LoginCompleted;
then later after the service returns biz object:
if (this.LoginCompleted != null)
{
this.LoginCompleted(this, new EventArgs() //this is where I would attach / pass my biz object no?);
}
Parent
ctrl_Login.LoginCompleted += ctrl_Login_LoginCompleted;
....snip....
void ctrl_Login_LoginCompleted(object sender, EventArgs e)
{
//get my object returned by login
}
So my question is what would be the "approved" method for getting the user object back to the parent? Create a property class that everything can access and put it there?
You would have to declare your event using EventHandler<T> where T is your class that derives from EventArgs:
public event EventHandler<LoginCompletedEventArgs> LoginCompleted;
LoginCompletedEventArgs could look like this:
public class LoginCompletedEventArgs : EventArgs
{
private readonly YourBusinessObject _businessObject;
public LoginCompletedEventArgs(YourBusinessObject businessObject)
{
_businessObject = businessObject;
}
public YourBusinessObject BusinessObject
{
get { return _businessObject; }
}
}
Usage would be like this:
private void RaiseLoginCompleted(YourBusinessObject businessObject)
{
var handler = LoginCompleted;
if(handler == null)
return;
handler(this, new LoginCompletedEventArgs(businessObject));
}
Please notice how I implemented RaiseLoginCompleted. This is a thread-safe version of raising the event. I eliminates a possible NullReferenceException that can occur in a race condition scenario where one thread wants to raise the event and another thread un-subscribes the last handler after the if check but before actually invoking the handler.
Well, you could do all that or you could define a delegate as your EventHandler and define your properties in its signature.
Such as:
public delegate void MyEventEventHandler(int prop1, string prop2, object prop3...);
public event MyEventEventHandler MyEvent;
I recommend use named tuples with EventHandler<TEventArgs>.
I like olddog's answer. Microsoft already has this delegate EventHandler< TEventArgs >.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
You don't need to inherits from EventArgs.
Declare your event handler with named tuples.
public event EventHandler<(int id, string message, object LoginObject)> LoginCompleted;
In your client code, assign method to the LoginCompleted event handler
option 1: use lambda
LoginCompleted += (o, e) =>
{
Console.WriteLine($"Hello, sender is {o.ToString()}! id is {e.id}, message is {e.message}, and LoginObject is {e.LoginObject.ToString()}. ");
};
option 2: call a local method
LoginCompleted += onLoginCompleted;
private static void onLoginCompleted (object sender, (int id, string message, object LoginObject) e)
{
Console.WriteLine($"Hello, sender is {sender.ToString()}! id is {e.id}, message is {e.message}, and LoginObject is {e.LoginObject.ToString()}. ");
}
I just wrote an example, please refer to my repo
I personally like Toni Petrina's approach (see https://coderwall.com/p/wkzizq/generic-eventargs-class). It differs from the accepted answer in that you don't have to create a special EventHandler class (e.g. LoginCompletedEventArgs).
(Note: I am using VS 2015 and C# v6. In older versions of Visual Studio and C#, you may have to add using System.Linq;)
Create a generic EventArgs<T> class that inherits from EventArgs...
class EventArgs<T> : EventArgs {
public T Value { get; private set; }
public EventArgs(T val) {
Value = val;
}
}
Declare your event handler...
public event EventHandler<EventArgs<object>> LoginCompleted;
Assuming you have declared and assigned an object named loginObject, add code to raise you event...
private void RaiseLoginCompleted() {
if (LoginCompleted != null)
LoginCompleted(this, new EventArgs<object>(loginObject));
}
In your client code, add the LoginCompleted event handler (uses Linq and calls a local method)...
LoginCompleted += (o, e) => onLoginCompleted(e.Value); // calls a local method
void onLoginCompleted(LoginObject obj) {
// add your code here
}
sometimes it sucks to create a class for merely passing a bool as a derived EventArgs! so you can simply use Action instead of EventHandler. you can pass any type and how many parameters you like (Action supports Up to 16).
class Raiser
{
public event Action<Raiser, bool,DateTimeOffset> OnCreate;
public void Create()
{
OnCreate?.Invoke(this, true,DateTimeOffset.Now);
}
}
class Listener
{
Raiser raiser;
public Listener()
{
raiser = new Raiser();
raiser.OnCreate += Raiser_OnCreate;
}
private void Raiser_OnCreate(Raiser arg1, bool arg2,DateTimeOffset dateTimeOffset)
{
throw new NotImplementedException();//Do Your works here
}
}
generally using Action and 'Func' are easier than Delegate.

How to declare generic event for generic delegate in c#

I have a user control which deals with fileupload. I have defined a delegate as follows
public delegate void FileUploadSuccess<T>(T value,FileUploadType F)
value can be a string as well as byte array. FileUploadType is an enum which tells which type of file was uploaded.
Now I have declared a event in usercontrol to raise this.
public event FileUploadSuccess<string> successString; //In case I want a file name
public event FileUploadSuccess<Byte[]> successStringImage; // In case I want a byte[] of uploaded image
What I wanted was a generic event
public event FileUploadSuccess<T> successString.
Except as part of generic types (i.e.
class Foo<T> { public event SomeEventType<T> SomeEventName; }
) there is no such thing as generic properties, fields, events, indexers or operators (only generic types and generic methods). Can the containing type here be generic?
To the outside world, an event in many ways looks like a field of the class. Just as you can't use an open generic type to declare a field, you can't use an open generic type to declare an event.
If you could leave the type open, then the compiler would have to compile in the event handler add and remove code for every possible type for your generic parameter T. A closed generic type can't be JIT compiled, because your event is not a type in its own right, rather is a part of an enclosing type.
That's impossible unless you define your type parameter in an enclosing class. For example:
public delegate void FileUploadSuccess<T>(T value, FileUploadType F)
public class FileUploader<T>
{
public event FileUploadSuccess<T> FileUploaded;
}
But this only moves your problem to another location, since now you would have to declare two instances of the FileUploader class:
FileUploader<string> stringUploader = new FileUploader<string>();
FileUploader<byte[]> stringUploader = new FileUploader<byte[]>();
This may not be what you want.
Why do you need a generic event? Can't you just use a normal event:
public delegate void FileUploadSuccess(object value);
and then
public event FileUploadSuccess Success;
In the Success event handler you will know the type of the object being passed:
public void SuccessHandler(object value)
{
// you know the type of the value being passed here
}
I don't think this is possible.
Event is like an instance of a delegate (roughly speaking), and an instance is a concrete implementation ( of a generic or a non-generic class).
For better understanding of delegates and event, you can refer to this SO discussion.
There is a generic EventHandler class in the .Net Framework just for this purpose:
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Counter c = new Counter(new Random().Next(10));
c.ThresholdReached += c_ThresholdReached;
Console.WriteLine("press 'a' key to increase total");
while (Console.ReadKey(true).KeyChar == 'a')
{
Console.WriteLine("adding one");
c.Add(1);
}
}
static void c_ThresholdReached(object sender, ThresholdReachedEventArgs e)
{
Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached);
Environment.Exit(0);
}
}
class Counter
{
private int threshold;
private int total;
public Counter(int passedThreshold)
{
threshold = passedThreshold;
}
public void Add(int x)
{
total += x;
if (total >= threshold)
{
ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
args.Threshold = threshold;
args.TimeReached = DateTime.Now;
OnThresholdReached(args);
}
}
protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
{
EventHandler<ThresholdReachedEventArgs> handler = ThresholdReached;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<ThresholdReachedEventArgs> ThresholdReached;
}
public class ThresholdReachedEventArgs : EventArgs
{
public int Threshold { get; set; }
public DateTime TimeReached { get; set; }
}
}
source: https://learn.microsoft.com/en-us/dotnet/api/system.eventhandler-1

Categories

Resources