I am working with a WCF service, calls to the service are invoked using an async method. As I am working with Xamarin studio the WCF proxy must be created using the silverlight tools (slsvcutil.exe) and as such this seems to be the only way to achieve what I am doing.
To make a call to the service I must make a call like so:
Soap.client.DoActionCompleted += Soap_client_DoActionCompleted;
Soap.client.DoActionAsync(parameter);
And then declare the delegate for DoActionCompleted like so:
void Soap_client_DoActionCompleted(object sender, DoActionCompletedEventArgs e)
{
Soap.client.DoActionCompleted -= Soap_client_DoActionCompleted;
// Do stuff
}
I have to remove Soap_client_DoActionCompleted after the call to prevent it from stacking further calls on it later.
The problem is I have many calls like this in my code, and it is beginning to get very messy and hard to follow the logic when writing the code. I would much prefer to be able to declare the code inline so it's much easier to follow, something like this:
Soap.client.DoActionCompleted += (sender, e) =>
{
Soap.client.DoActionCompleted -= x;
// Do stuff
}
Soap.client.DoActionAsync(parameter);
In the above snippet, I'd like to be able to pass the delegate that it is inside to remove it then and there, but the problem is I have no idea if this is even possible.
While I know there are almost certainly better ways of doing the above, I'm a little trapped by the fact that I am required to use slsvcutil.exe to generate the proxy for the service, and writing the proxy manually isn't an option as the service gets regular updates.
It's kind of possible if you assign the lambda expression to a delegate variable first. It just won't be as nice as what you're hoping for.
And even then, to avoid a use of unassigned local variable compiler error, you have to first initialize the delegate variable to null before you can assign the lambda expression that will have a reference to itself.
So, using your example, it would look like this (I'm assuming the type of DoActionCompleted is EventHandler. Simply adjust if I'm wrong):
EventHandler eventHandler = null;
eventHandler = (sender, e) =>
{
Soap.client.DoActionCompleted -= eventHandler;
// do stuff
};
Soap.client.DoActionCompleted += eventHandler;
Soap.client.DoActionAsync(parameter);
It's up to you to decide if it's worth it for you to use this pattern.
Related
If I subscribe a new Method to a Handler and Press "Tab" twice after the "+=" VS will implement a Body like:
public class A {
public A(){
button1.Click += OnButton1Click();
}
private OnButton1Click(object sender, EventArgs e){
}
}
How can I change VS to create the Body of the Method as public instead of private?
Kind regards,
Asat0r
How can I change VS to create the Body of the Method as public instead
of private?
Not sure if there is any option but why you would like to do that? event handlers are not meant to be public. If you want to call the logic inside handler from some other type then refactor that logic to a helper method and re-use that instead probably.
Other answers have suggested that the method shouldn't be public.
Broadly speaking, I reject that - at least without further justification. If you want to expose a method with exactly that signature, I'd normally go ahead and do so. Two caveats around that though:
Normally the signature of an event handler isn't useful except as an event handler; the parameters aren't generally useful when calling the method directly. There are exceptions to that as ever.
If you subscribe an event handler with a public method, any other code can unsubscribe that event handler by creating an equal delegate. If you control all the code in the application, that's not much of a problem.
Both cases can be solved by exposing a public method with exactly the parameters you want (which could still be event handler parameters, but often won't be) and then using a lambda expression to subscribe to the event:
// I want to be able to call this from other code
public void SaveItem()
{
// ...
}
// Hook up the event handler using a lambda expression
saveButton.Click += (sender, args) => SaveItem();
On the other hand, if neither of the bullet points are an issue, go ahead and make the method public manually. I wouldn't expect there to be a VS shortcut to do so, but simply specifying the public modifier is pretty simple.
I am attempting to bind a method to the click event of a button.
var controlEvent = button.GetType().GetEvent("Click");
var eventMethod = GetType().GetMethod("button_Click");
var handler = Delegate.CreateDelegate(controlEvent.EventHandlerType, button, eventMethod);
void button_Click(object sender, EventArgs e) { }
When I call CreateDelegate I get
Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type
I feel like I am passing the wrong types into CreateDelegate but am unsure exactly.
There is no reason to use reflection here. Been a while since I've worked with winforms/webforms but it should look something like this:
button.OnClick += button_Click;
I'll comment further that button_Click is not a good name for a method unless what it does is click a button. That convention comes from some really old Microsoft guidance and it's just not good. Name functions after what they do, not how they're used. Consider how much better this reads
button.OnClick += showCalculations;
I usually go a step further and get rid of the dumb (object, EventArgs) parameters as well (unless I'm using these)
button.OnClick += (o,e) => showCalculations();
//...
void showCalculations() {
//...
}
The second parameter to Delegate.CreateDelegate is the class instance to call the method on.
Your method isn't defined on button, so you get an error.
You need to pass this.
For a simple example, if I had some sort of button UI class, could I write a function that takes an expression that points to its Click event handler:
SomeMethod<SomeButtonClass>(button => button.Click);
I'm trying to eliminate some magic strings currently being used for a system to make events awaitable. The code in question is derived from a blog post by Frank Krueger (a worthwhile read, if you want some background).
public static Task<TEventArgs> GetEventAsync<TEventArgs>(this object eventSource, string eventName) where TEventArgs : EventArgs {
//...
Type type = eventSource.GetType();
EventInfo ev = type.GetEvent(eventName);
//...
}
While the specifics inside probably aren't important, the full method allows you to use an Event triggering as the completion source for a Task, making it easier to manage with await. For some class that raises an event, you can tie into a Task based on that event with a simple call.
Task<EventArgs> eventTask = someEventCausingObject.GetEventAsync<EventArgs>("SomeEventHandler");
// traditionally used as someEventCausingObject.SomeEventHandler += ...;
await eventTask;
// Proceed back here when SomeEventHandler event is raised.
I have been using this happily for a couple projects, but it has its drawbacks, one of the biggest being the use of hard-coded event name strings. This makes event name changes turn into runtime exceptions, and determining usage of the event is difficult.
I started trying to make a version that would allow the EventHandler to be passed in as part of an Expression with the goal of something like this:
await someEventCausingObject.GetEventAsync<EventCausingClass, EventArgs>(x => x.SomeEventHandler);
...with the corresponding method signature...
public static Task<TEventArgs> GetEventAsync<TSource, TEventArgs>(this TSource eventSource, Expression<Func<TSource, EventHandler>> eventHandlerExpression) where TEventArgs : EventArgs {
//...
}
Unfortunately, the lambda expression in the calling code causes a compile error:
Error CS0070: The event `SomeEventHandler' can only appear on the left hand side of += or -= when used outside of the type `EventCausingClass'.
This makes some sense given how event handlers are typically used, but I was hoping to find a better solution going forward than the pre-specified string name. It seems searches for combinations of "expression" and "eventhandler" all tend to be polluted with people describing lambda expressions for beginning += event handler assignment. I'm hoping I am missing something obvious here.
No, it is not possible to target an event. Basically event is not a real type member, but just C# syntax which produces add_EventName and remove_EventName methods pair.
You could try refer to these internal methods name, but it's not possible in C# - http://msdn.microsoft.com/en-us/library/z47a7kdw.aspx
There are many similar questions in SO, with the same answer NO - like this one from Jon Skeet https://stackoverflow.com/a/4756021/2170171
If you're real crazy, you can try something like
private static void Subscribe(Action addHandler)
{
var IL = addHandler.Method.GetMethodBody().GetILAsByteArray();
// Magic here, in which we understand ClassName and EventName
???
}
with usage like
Subscribe(() => new Button().Click += null);
You could try using Cecil http://www.mono-project.com/Cecil for analyzing IL, or implement your own logic as it should not be too hard for predictable line of code.
I don't think that it is good solution though, as it just replaces one headache (proper event naming) with another one (proper Subscribe calling). Though, it will help with rename stuff.
I'm trying to understand the difference between:
someObject.SomeEventName += OnEventHappend;
private void OnEventHappened(object sender, EventArgs e)
{
}
AND
someOBject.SomeEventName += ( o, e) => { };
Is there a difference and does it matter one way or the other.
The second is called a "lambda". It's a way of creating an anonymous function.
Other than the fact that the second function doesn't have a name, and thus can't be used anywhere else, while the first does have a name and could be used somewhere else, there aren't really any differences.
The compiler will end up creating a class and a method within that class to represent the lambda, so technically it will have a name, you just won't be allowed to use it in your code.
Probably the most significant point to note is that lambdas can close over variables. This is where the lambda body refers to a varaiable outside of it's own scope, i.e:
int value = 5;
someObject.SomeEvent += (s,e) => { Console.WriteLine(value);};
The same functionality can be accomplished using entirely named methods (after all the compiler will refactor that code into named methods/objects at some point) but the end result will be slightly more complex than just one new method in the class.
Well one obvious difference is that the later is an anonymous event handler implemented as a statment lambda, so you can't easily unsubscribe it at a later date. The advantage of it though is its succinctness.
I'm trying to use events to use the nice += -= syntax.
Here is my problem:
I have a lot of different events, so instead of creating many events, I would like to use only one,
where the registrant additionally supplies an additional parameter (string[]) detailing which (sub)events one wants to listen to.
The is basically what I'm doing now:
public delegate void dMethod(int index);
private Dictionary<string, dMethod> events;
public void RegisterEvents(dMethod m, string[] subEvents){
foreach(string subEvent : subEvents){
events[subEvent] += m;
}
}
Instead I would like to do something like this:
public delegate void dMethod(int index);
private Dictionary<string, dMethod> events;
public event dMethod Events(string[] subEvents) {
add {
foreach(string subEvent : subEvents){
events[subEvent] += value;
}
}
remove {
foreach(string subEvent : subEvents){
events[subEvent] -= value;
}
}
}
Is something like passing additional parameters upon event registration somehow possible?
The simplest solution I thought of was to use the delegates return value (changing it to string[]).
But that is just hacky and not worth it.
If it is not possible, is there some way I can define += -= myself?
Why exactly don't you want to use one event per... event ?
What you're trying to do would add an indirection step (which will hurt performance), and more importantly deny you the advantages of compile-time error detection and code analysis (what if your string doesn't match an existing event? wouldn't it be nice to find out about the error at compile-time rather than runtime? What about the "find all references" tool?)
If all of your events are really separate events (which have nothing to do with one another), you should create those events. Creating one single event for all kinds of things would be a bit like creating one big sql table with 255 Columns instead of a true database design.
On the other hand, you may be able to find an abstraction of all those events, and add an argument to the event handler (not to the subscription logic) specifying what sub-event has been called. See IBindingList for an example of this technique. Note the use of an enumeration instead of strings to avoid spelling mistakes.
Could you give us some example (business-wise) of the events you're working on ?
The simplest solution I thought of was
to use the delegates return value
(changing it to string[]).
I dont quite understand what you mean by this. Would the event handler return the events it wants to handle?
I don't think that what you want to do is an option. It may be possible (if you abandon the .Net event system) to use the += syntax by creating a custom class and some implicit conversion operators to produce code something like:
SomePropertyThatIsntARealEvent[new string[] {"event1", "event2"}] += someDelegate
// or even
SomePropertyThatIsntARealEvent["event1"]["event2"] += someDelegate
Note that, if it's even doable, it would be messy and hacky and not really worth it anyways. Also, unfortunately, you can't use params for indexers.
I would recommend that you just stick with the un-pretty way.
This isn't an answer to your main question, but for ease of use I'd add the "params" keyword to your method, like so:
public void RegisterEvents(dMethod m, params string[] subEvents)
This will let you call the method like this:
RegisterEvents(m, "Bob", "Doug", "Susie");
instead of:
RegisterEvents(m, new string[] { "Bob", "Doug", "Susie" });