In C#/Unity, is there a way to pass more arguments to an event handler that were not specified in the delegate?
Assume that I cannot modify the delegate. (It comes from an external class.)
I need to be able to do this so I can unsubscribe from my handler later (onClick -= HandleClick somewhere else in the code).
Example:
delegate void A(str s);
event A onClick;
Awake() {
onClick += HandleClick;
}
void HandleOnClick(str s, int a) {
// How to get access to this second argument a here?
}
I know I can do the following:
int a = 10;
onClick += (s) => HandleOnClick(s, 10);
But this won't let me unregister the lambda (since it's an anonymous delegate) if I wire it up this way.
One way you can try to use a variable to hold the delegate reference which is able to subscribe or unsubscribe from the event.
Awake() {
int a = 10;
var e1 = (s) => HandleOnClick(s, a);
onClick += e1;
onClick -= e1;
}
Related
This is the code I want to realize.
(Maybe it's not correct, or can't be realized ???)
var buttons = this.buttons;
for (int i = 0; i < buttons.Length; ++i)
{
var button = buttons[i];
// I want to pass current button to onClick,
// but onClick is a delegate with no param just like System.Action.
button.onClick += OnClick;
}
void OnClick(Button button)
{
// Do something
}
You can forward events using lambdas. It's not the most pretty thing in the world. You can subscribe to the onClick event of your button using () => OnClick(button);. This lambda is a void delegate with 0 parameters, just like an Action, and therefor can be used to subscribe to the onClick event.
Essentially, you take the value of the local variable button and use it in the lambda, which is only going to be called when the event is invoked.
Your line should look like this: button.onClick += () => OnClick(button);
For Sample ....
SampleClass :
public class SampleClass
{
public delegate void BeforeEditorHandle();
public event BeforeEditorHandle OnBeforeEditor;
}
MainMethod
static void Main(string[] args)
{
SampleClass sc = new SampleClass();
// Add Event
sc.OnBeforeEditor +=new SampleClass.BeforeEditorHandle(sc_OnBeforeEditor);
// Remove Event
sc.OnBeforeEditor -= new SampleClass.BeforeEditorHandle(sc_OnBeforeEditor);
}
And , if I add the event by dynamic like this ...↓
sc.OnBeforeEditor += () => { };
Should I remove the event like ↓
sc.OnBeforeEditor -= () => { };
But I think this is very ugly when I have too much sources in the event....
Can anybody tell me the best way to remove the event please ?
You can assign the event handler/lambda to a variable which you can then subscribe and unsubscribe:
var myHandler = () => { };
sc.OnBeforeEditor += myHandler;
sc.OnBeforeEditor -= myHandler;
I'm pretty sure your code here won't work:
And , if I add the event by dynamic like this ...↓
sc.OnBeforeEditor += () => { };
Should I remove the event like ↓
sc.OnBeforeEditor -= () => { };
This is because restating the lambda creates a new different lambda.
You need to store the old reference and use it to unsubscribe:
BeforeEditorHandle myHandler=() => { }
sc.OnBeforeEditor += myHandler;
...
sc.OnBeforeEditor -= myHandler;
For easier unsubscribing you can collect your event handlers in a collection (For example List<BeforeEditorHandle>).
From MSDN:
It is important to notice that you
cannot easily unsubscribe from an
event if you used an anonymous
function to subscribe to it. To
unsubscribe in this scenario, it is
necessary to go back to the code where
you subscribe to the event, store the
anonymous method in a delegate
variable, and then add the delegate to
the event. In general, we recommend
that you do not use anonymous
functions to subscribe to events if
you will have to unsubscribe from the
event at some later point in your
code. For more information about
anonymous functions, see Anonymous
Functions (C# Programming Guide).
I have the following code where SprintServiceClient is a reference to a WCF Service-
public class OnlineService
{
private SprintServiceClient _client;
public OnlineService()
{
_client = new SprintServiceClient();
}
public void AddMemberToTeam(MemberModel user, int projectId, Action<int> callback)
{
_client.AddMemberToTeamCompleted += (s, e) => callback(e.Result);
_client.AddMemberToTeamAsync(user.ToUser(), projectId);
}
}
the problem is that every time AddMemberToTeam is called it adds another callback to client.AddMemberToTeamCompleted
i.e the first time AddMemberToTeam is called the callback is called once, the second time AddMemberToTeam is called the callback is called twice ect.
Is there any way to remove the eventhandler from AddMemberToTeamCompleted once the eventhandler has been called or use another method which takes in the callback?
You can refer to your anonymous method from inside itself as long as you assign a delegate to a variable first:
EventHandler<SomeEventArgs> handler = null;
handler = (s, e) =>
{
_client.AddMemberToTeamCompleted -= handler;
callback(e.Result);
};
_client.AddMemberToTeamCompleted += handler;
Note that you need to declare the variable and assign it separately or the compiler will deem it uninitialized when you come to use it inside the method body.
The trick to making a self-unsubscribing event-handler is to capture the handler itself so you can use it in a -=. There is a problem of declaration and definite assignment, though; so we can't do something like:
EventHandler handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler; // <===== not yet defined
};
So instead we initialize to null first, so the declaration is before the usage, and it has a known value (null) before first used:
EventHandler handler = null;
handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler;
};
_client.AddMemberToTeamCompleted += handler;
No there is no way,
Apparantly Tim and Marc have another nice solution
But you can always just name them, and do the -= on the named eventhandler on this method ;)
Guessing your event:
_client.AddMemberToTeamCompleted += OnAddMemberToTeamCompleted;
and
public void OnAddMemberToTeamCompleted(object sender, EventArgs args)
{
_client.AddMemberToTeamCompleted -= OnAddMemberToTeamCompleted;
callback(e.Result)
}
Next problem is getting this callback in your listener. Perhaps putting it on a Property in the EventArgs (but that feels kinda dirty, I agree)
I'm using global variable named "client"
For example
client.getPagesCompleted += (s, ee) =>
{
pages = ee.Result;
BuildPages(tvPages.Items, 0);
wait.Close();
};
client.getPagesAsync(cat.MainCategoryID);
I need to clear handlers for getPagesCompleted and set another handler. How to easy clear handles?
I know client.getPagesCompleted-=new EventHandler(...). But it is very difficult. I need easy way.
I'm using client.getPagesCompleted=null but error shown. "only use += / -+"
The only way to remove an event handler is to use the -= construct with the same handler as you added via +=.
If you need to add and remove the handler then you need to code it in a named method rather using an anonymous method/delegate.
You don't have to put your event handler in a separate method; you can still use your lambda function, but you need to assign it to a delegate variable. Something like:
MyEventHandler handler = (s, ee) =>
{
pages = ee.Result;
BuildPages(tvPages.Items, 0);
wait.Close();
};
client.getPagesCompleted += handler; // Add event handler
// ...
client.getPagesCompleted -= handler; // Remove event handler
Save the event object to a variable, and use -= to unsubscribe.
When you subscribe to an event in .NET, the subscription is added to a multicast delegate. When the event is fired, the delegates are called in the order they were subscribed.
I'd like to override the subscription somehow, so that the subscriptions are actually fired in the reverse order. Can this be done, and how?
I think something like this might be what I need?:
public event MyReversedEvent
{
add { /* magic! */ }
remove { /* magic! */ }
}
You don't need any magic; you just need to reverse the addition.
Writing delegate1 + delegate2 returns a new delegate containing the method(s) in delegate1 followed by the methods in delegate2.
For example:
private EventHandler myReversedEventField;
public event EventHandler MyReversedEvent
{
add { myReversedEventField = value + myReversedEventField; }
remove { myReversedEventField -= value; }
}
You don't need any magic in the remove handler, unless you want to remove the last occurrence of that handler instead of the first. (In case the same handler was added twice)
Controlling When and If a Delegate Fires Within a Multicast Delegate
The following method creates a multicast delegate called allInstances and then uses GetInvocationList to allow each delegate to be fired individually, in reverse order:
public static void InvokeInReverse()
{
MyDelegate myDelegateInstance1 = new MyDelegate(TestInvoke.Method1);
MyDelegate myDelegateInstance2 = new MyDelegate(TestInvoke.Method2);
MyDelegate myDelegateInstance3 = new MyDelegate(TestInvoke.Method3);
MyDelegate allInstances =
myDelegateInstance1 +
myDelegateInstance2 +
myDelegateInstance3;
Console.WriteLine("Fire delegates in reverse");
Delegate[] delegateList = allInstances.GetInvocationList();
for (int counter = delegateList.Length - 1; counter >= 0; counter--)
{
((MyDelegate)delegateList[counter])();
}
}
One option would be to handle this when you raise the event. You can get the event subscribers via Delegate.GetInvocationList, and just call each delegate in reverse order yourself.