I am trying to figure out how to wait for a callback from a class function (I guess).
My class looks like this:
public class DataLogOut
{
public delegate void OnLogoutResponse(ResponseData data);
public event OnLogoutResponse onLogoutResponse;
public static void LogoutPlayer()
{
new EndSessionRequest().SetDurable(true).Send((response) => {
if (!response.HasErrors)
{
GS.Reset();
if (onLogoutResponse != null)
{
onLogoutResponse(new ResponseData()
{
//data = response
});
}
}
else
{
if (onLogoutResponse != null)
{
onLogoutResponse(new ResponseData()
{
errors = response.Errors,
hasErrors = response.HasErrors
});
}
}
});
}
}
Now I am calling the "LogOutPlayer" like this:
public void LogOut()
{
DataLogOut.LogoutPlayer();
DataLogOut.onLogoutResponse += onLogoutResponse;
}
Now i am getting errors in both scripts.
In the first I am getting: Delegate invocation can be simplified
... and in the second script I am not allowed to do this:
DataLogOut.onLogoutResponse += onLogoutResponse;
Really hop someone can help me with this and thanks in advance :-)
There are several issues in your code. Please see comments:
public class DataLogOut
{
// No need for this, we will use "EventHandler"
// public delegate void OnLogoutResponse(ResponseData data);
//public event OnLogoutResponse onLogoutResponse; -> replaced by
public event EventHandler<ResponseData> onLogoutResponse;
// Convenience Method to fire the event
protected virtual void OnLogoutResponse( ResponseData data )
{
var handler = onLogoutResponse;
if( handler != null ){
handler( this, data );
}
}
// Let's simplify it by making it non-static
//public static void LogoutPlayer()
public void LogoutPlayer
{
new EndSessionRequest().SetDurable(true).Send((response) => {
if (!response.HasErrors)
{
GS.Reset();
OnLogoutResponse(new ResponseData()
{
//data = response
});
}
else
{
OnLogoutResponse(new ResponseData()
{
errors = response.Errors,
hasErrors = response.HasErrors
});
}
});
}
}
Usage:
public void LogOut()
{
// I made it non-static, so we need an instance ...
var logout = new DataLogout();
// first register for the event, then have it fired.
logout.onLogoutResponse += onLogoutResponse;
// ^-- You tried to register the handler on the class. Which failed,
// because the event was not static.
logout.LogoutPlayer();
}
// the handler's signature now must look like this:
public void onLogoutResponse( object sender, ResponseData data ){
// your code here
}
If you want to keep it static, then make the event static, too:
public static event EventHandler<ResponseData> onLogoutResponse;
then you need to make the convenience event trigger static, too
protected static void OnLogoutResponse( ResponseData data )
{
var handler = onLogoutResponse;
if( handler != null ){
handler( typeof(DataLogout), data ); // cannot use "this", of course in static context.
}
}
and then can use it as in your example:
public void LogOut()
{
// first register for the event, then have it fired.
DataLogout.onLogoutResponse += onLogoutResponse;
// ^-- You tried to register the handler on the class. Which failed,
// because the event was not static.
DataLogout.LogoutPlayer();
}
// the handler's signature now must look like this:
public void onLogoutResponse( object sender, ResponseData data ){
// your code here
}
Related
Let say I have an action like this:
public Action OnSomeAction;
I would like to be able to subscribe to this action from outside of the class, but not be able to invoke it:
OnSomeAction.Invoke();
Is there a way to do this without making the action private and creating methods for subscribing and unsubscribing like this:
private Action _someAction;
public void Subscribe(Action listener)
{
_someAction += listener;
}
public void Unsubscribe(Action listener)
{
_someAction -= listener;
}
private void Invoke()
{
_someAction.Invoke();
}
Are you looking for event?
public class MyClass {
// Let firing event be private
private void onMyAction() {
Action action = MyAction;
if (action != null)
action();
}
public void FireDemo() {
onMyAction();
}
//TODO: I've put Action, but, probably, EventHandler will be a better choice
// while event itself (subscribe / unsubscribe) being public
public event Action MyAction;
}
Demo:
MyClass myClass = new MyClass();
var first = () => {Console.WriteLine("I'm the first")};
var second = () => {Console.WriteLine("I'm the second")};
var none = () => {Console.WriteLine("I should not fire")};
myClass.MyAction += first;
myClass.MyAction += second;
myClass.MyAction += none;
// Unsubsribe
myClass.MyAction -= none;
// Direct attempt will NOT compile:
// myClass.MyAction();
myClass.FireDemo();
Although this looks a lot like events. You can achieve it using Action too. Consider following code which is almost same as you suggested:
private List<Action> _someActionList = new List<Action>();
public void Subscribe(Action listener)
{
_someActionList.Add(listener);
}
public void Unsubscribe(Action listener)
{
_someActionList.Remove(listener);
}
private void Invoke()
{
foreach(action in _someActionList)
{
action();
}
}
I hope this is exactly what you want to do. If not then please elaborate further.
Given the following code:
public delegate void Signal();
public static class SignalExtensions
{
public static void SafeInvoke(this Signal signal)
{
Signal copy = signal;
if (copy != null)
{
copy();
}
}
}
public class RootEventSource
{
public event Signal RootEvent;
public void Raise()
{
this.RootEvent.SafeInvoke();
}
}
public class EventForwarder
{
private readonly RootEventSource rootEventSource;
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
// this is the critical part
this.rootEventSource.RootEvent
+= () => this.AnotherEvent.SafeInvoke();
}
public event Signal AnotherEvent;
// just an example of another method which is using the root event source
public override string ToString()
{
return this.rootEventSource.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var rootEventSource = new RootEventSource();
var eventForwarder = new EventForwarder(rootEventSource);
eventForwarder.AnotherEvent += HandleAnotherEvent;
rootEventSource.Raise();
Console.WriteLine("done");
Console.ReadKey();
}
private static void HandleAnotherEvent()
{
Console.WriteLine("received AnotherEvent");
}
}
This results in the output:
received AnotherEvent
done
Now I make a slight change to the implementation of EventForwarder to use a method group for forwarding the event:
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
}
The output becomes:
done
So AnotherEvent is not raised.
Until now i would have considered the two lines:
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
this.rootEventSource.RootEvent += () => this.AnotherEvent.SafeInvoke();
as being equivalent. It seems they're not.
So what is the difference? Plus why is the event not being raised?
PS: while usually R# suggests to replace () => this.AnotherEvent.SafeInvoke(); by this.AnotherEvent.SafeInvoke it doesn't do so here. So apparently it knows that it should not do it here.
When you assign a method group to event like this:
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
you in fact create a delegate from method SignalExtensions.SafeInvoke which as a parameter takes your this.AnotherEventdelegate object. Since it is initially null, you create a delegate with null parameter. This null value will of course never change, since delegates are immutable.
If you want to forward an event you should maybe do it like this:
public class EventForwarder
{
private readonly RootEventSource rootEventSource;
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
}
public event Signal AnotherEvent
{
add { this.rootEventSource.RootEvent += value; }
remove { this.rootEventSource.RootEvent -= value; }
}
}
I am making a game and I'm trying to create an way for objects to handle collisions with each other. I want to do something like:
//Imaginary C#
public SomethingThatCollides()
{
CollisionEvent<ObjectA> += CollisionWithObjA;
CollisionEvent<ObjectB> += CollisionWithObjB;
}
void CollisionWithObjA(ObjectA other)
{
//Do something
}
void CollisionWithObjB(ObjectB other)
{
//Do something else
}
When, say, CollisionEvent<ObjectA> is raised (perhaps by some collision checking code), CollisionWithObjA should get called. Same for CollisionWithObjB; when a collision with ObjectB is detected, it will raise the CollisionEvent<ObjectB> event which results in CollisionWithObjB getting called.
Is something like this possible?
Here is the thing, if class is generic and it has static field, it can work like a dictionary with key being type
public class Something {
public class EventsHolder<T>
{
static event Action<T> CollideEvent;
}
public void AddEvent<T>(Action<T> collisionEvent)
{
EventsHolder<T>.CollideEvent = collisionEvent;
}
public void RaiseCollision<T>(T Obj)
{
var Event = EventsHolder<T>.CollideEvent;
if (Event != null) Event(Obj);
}
}
Downside is that it uses static fields which can be inapropriate.
In this case you can use code #Daniel posted.
You can't really create a generic event like that. I suggest you create a special event arguments class that also encapsulates the collided object and check for its type in the event handler method:
public class CollisionEventArgs : EventArgs {
public object Object {
get; private set;
}
// ...
}
You'll need a special dispatcher method to use it:
class SomethingThatCollides {
public SomethingThatCollides(CollisionManager cm) {
cm.CollisionEvent += CollisionWithObj;
}
void CollisionWithObj(object sender, CollisionEventArgs args) {
if (args.Object is ObjectA) {
CollisionWithObjA((ObjectA)args.Object);
}
else if (args.Object is ObjectB) {
CollisionWithObjB((ObjectB)args.Object);
}
}
// ...
}
Or, you can try to solve this with double-dispatching, without using C# events. Look at wikipedia for a collision example.
That's uggly, but...You could have a dicionary of events by type:
Dictionary<Type, object> MyEventsByType;
event Action<A> CollisionEventA;
event Action<B> CollisionEventB;
event Action<C> COllisionEventC;
void Initialize()
{
MyEventsByType = new Dictionary<Type, object>();
MyEventsByType.Add(typeof(A), CollisionEventA);
MyEventsByType.Add(typeof(B), CollisionEventB);
MyEventsByType.Add(typeof(C), CollisionEventC);
}
void RaiseCollision<T>(T Obj)
{
Action<T> Event = (Action<T>)MyEventsByType[typeof(T)];
if (Event != null) Event(Obj);
}
Goal: To change a image on a form when either udp or tcp uses its send method
Problem: I have no idea how to get the event, eventhandler and delegates set up correctly
Send Interface
interface ISendData
{
void Send();
}
Tcp Connection class
//Need some type of delegate??
public class TCPconnection : ISendData
{
void Send()
{
//how invoke/fire a send Event?
}
}
UDP Connection class
//Need some type of delegate??
public class UDPConnection : ISendData
{
void Send()
{
//how invoke/fire a send event?
}
}
the winform which 'should' subscribe to seeing the fired events
public class myForm
{
private DataWatcher datawatcher = new DataWatcher();
private Image statusIndicator = null;
public myform()
{
initComponents();
datawatcher.DataSendActive += new DataWatcherSendHandler(DataSending);
datawatcher.DataSendInactive += new DataWatcherSendHandler(NoDataSending);
}
public void DataSending(object sender, DataWatcherArgs e)
{
statusIndicator = Properties.resources.greenLight;
}
public void NoDataSending(object sender, DataWatcherArgs e)
{
statusIndicator = Properties.resources.redLight;
}
}
The Event/Event handler?? But I really have no Idea what I'm doing here to make this work
public delegate void EventHandler(object sender, EventArgs e);
class DataWatcher
{
public event EventHandler DataSendActive;
public event EventHandler DataSendInactive;
protected virtual void onDataSendActive(System.EventArgs e)
{
if (DataSendActive != null)
{
DataSendActive(this, e);
}
}
protected virtual void onDataSendInactive(System.EventArgs e)
{
if (DataSendInactive != null)
{
DataSendInactive(this, e);
}
}
}
There are many conventions used to do this. Here's my little implementation.
public enum ActivityState
{
Sending,
Receiving,
Idle
}
public interface IDataTransferManager
{
// This event will fire when the activity state changes.
// note that Action<T> is introduced in .NET 3.5
// if you're using .NET 2.0, you can use a delegate.
event Action<ActivityState> DataActivityStateChange;
void Send(byte[] data);
//byte[] Receive();
// ... more methods ... //
}
Now the TcpConnection class will implement this.
public class TcpConnection : IDataTransferManager
{
public event Action<ActivityState> DataActivityStateChange;
public void Send(byte[] data)
{
// we're sending data. fire the change event
FireDataActivityStateChange(ActivityState.Sending);
//TODO: send the data
// we're done sending. Fire the change event
FireDataActivityStateChange(ActivityState.Idle);
}
private void FireDataActivityStateChange(ActivityState state)
{
// helper method, so I don't have to check the event
// to avoid null reference exceptions.
if (DataActivityStateChange != null)
DataActivityStateChange(state);
}
}
Here's the setup for your Form.
class MyForm // :Form
{
IDataTransferManager dataManager;
public MyForm()
{ // here, usually an instance will be passed in,
// so there's only one instance throughout the application.
// let's new up an instance for explanation purposes.
dataManager = new TcpConnection();
dataManager.DataActivityStateChange += (state) =>
{
// NOTE: if you don't like inline,
// you can point this labda to a method.
switch (state)
{
case ActivityState.Sending:
// change the image to the spinning toilet ball
break;
case ActivityState.Receiving:
// change the image to the spinning toilet ball, but reverse :P
break;
case ActivityState.Idle:
// hide it ?
break;
}
};
}
}
Here is a simple example of how you could implement an event for sending and not sending and subscribe to it
public class Connection
{
//Set up an event
public event EventHandler DataSending;
public event EventHandler DataNotSending
//This method will trigger the event for sending
private void OnDataSending()
{
if (DataSending!= null) { DataSending(this, EventArgs.Empty); }
}
//this method will trigger the event for finished sending
private void OnDataNotSending()
{
if (DataNotSending!= null) { DataNotSending(this, EventArgs.Empty); }
}
//This method performs your send logic
public void Send()
{
//Call your method that tells the event to be raised
OnDataSending();
//Then put your send code
OnDataNotSending(); //we're done!
}
}
This is how you use it in a consuming program
public class myForm
{
//This method is the one that sets up the
//instance and subscribes to the event
public myForm()
{
Connection con = new Connection();
con.DataSending += new EventHandler(con_DataSending);
con.DataNotSending += new EventHander(con_DataNotSending);
}
void con_DataSending(object sender, EventArgs e)
{
//Put your subscription logic here.
//Whatever you want to do in response to a send
}
void con_DataNotSending(object sender, EventArgs e)
{
//Put your subscription logic here.
//Respond to it not sending
}
}
I have a repository that uses components that report events.
I want to show the reported events in the front end.
This is the repository:
public interface IXmlRepository
{
//irrelevant stuff removed
event EventHandler TraceEventHandler;
}
public class XmlPanelRepository : IXmlRepository
{
public XmlPanelRepository()
{
public event EventHandler TraceEventHandler;
var panelCom = new PanelCom(); // this is a COM object that connects to a device
// when something happens in the COM object it reports it.
panelCom.Trace += panelCom_Trace;
// I want to bubble the trace events up to my UI.
TraceEventHandler += TraceEventHandler_Tracing;
}
private void TraceEventHandler_Tracing(object sender, EventArgs e)
{
// what do I do here?
}
void panelCom_Trace(string message)
{
if (TraceEventHandler!= null) TraceEventHandler.Invoke(this, new EventArgs());
}
}
My UI uses a Service to interface with the repository. The service is defined as:
public interface IXmlConfigurationService
{
//irrelevant stuff removed
event EventHandler TraceEventHandler;
}
public class XmlConfigurationService : IXmlConfigurationService
{
public event EventHandler TraceEventHandler;
public XmlConfigurationService(IXmlRepository configurationRepository)
{
_configurationRepository.TraceEventHandler += ConfigurationRepository_TraceEventHandler;
}
void ConfigurationRepository_TraceEventHandler(object sender, EventArgs e)
{
// this never gets hit.
if (TraceEventHandler != null) TraceEventHandler.Invoke(sender, e);
}
}
If I can get this working, I presume I can follow the same steps to get the UI displaying event reports.
How can I get the Service to report the events that are occurring in the repository?
If I understand correctly, you would need to fulfil your event. This is akin to the Observable fulfilling all listening observers in the Observer Pattern. But for events written like this:
public XmlPanelRepository()
{
public event EventHandler TraceEventHandler;
var panelCom = new PanelCom(); // this is a COM object that connects to a device
// when something happens in the COM object it reports it.
panelCom.Trace += panelCom_Trace;
// I want to bubble the trace events up to my UI.
TraceEventHandler += TraceEventHandler_Tracing;
}
private void TraceEventHandler_Tracing(object sender, EventArgs e)
{
if (TraceEventHandler != null)
{
TraceEventHandler(this, e);
}
}
However, perhaps you should name the event something other than TraceEventHandler because you are now exposing an event (which usually is expressed in the past tense).
It is also worth noting that you can potentially change the event args as you bubble up (if you want to). EventHandler(TEventArgs) can help with this.
You're forgetting to use delegates...
Try something like this:
public interface IXmlRepository
{
//irrelevant stuff removed
event EventHandler TraceEventHandler;
}
public class XmlPanelRepository : IXmlRepository
{
public delegate void EventHandler(string parameter1, string parameter2);
public event EventHandler TraceEventHandler;
public XmlPanelRepository()
{
var panelCom = new PanelCom(); // this is a COM object that connects to a device
// when something happens in the COM object it reports it.
panelCom.Trace += panelCom_Trace;
}
void panelCom_Trace(string message)
{
if (EventHandler != null)
EventHandler("Event was hit, here's the message:", message);
}
}
public interface IXmlConfigurationService
{
//irrelevant stuff removed
}
public class XmlConfigurationService : IXmlConfigurationService
{
public XmlConfigurationService(IXmlRepository configurationRepository)
{
_configurationRepository.TraceEventHandler += ConfigurationRepository_EventHandler;
}
void ConfigurationRepository_EventHandler(string parameter1, string parameter2);)
{
// Do something with your parameters.
Response.Write(parameter1 + parameter2);
}
}