I'm trying to resolve dynamically loaded assemblies, but the AssemblyResolve event does not appear to work with anonymous functions.
private void Load() {
ResolveEventHandler resolve = (sender, args) => Assembly.LoadFile(pathToDependency);
AppDomain.CurrentDomain.AssemblyResolve += resolve;
AppDomain.CurrentDomain.AssemblyResolve += this._AssemblyResolve;
Assembly.LoadFile(pathToDll);
}
private void _AssemblyResolve(Object sender, ResolveEventArgs args) {
return Assembly.LoadFile(pathToDependency);
}
this._AssemblyResolve and resolve are virtually the same function, except that the anonymous resolve function creates a closure that uses a variable defined in the Load method.
For some reason, this._AssemblyResolve gets called, but resolve doesn't. Why?
I can't really rely on the _AssemblyResolve method because the method needs to know about a variable that is defined in the Load method. I can use a workaround, but I still don't understand why the anonymous function doesn't work.
Related
Ok, I am working with public static UnityActions for event driven multi delegate execution and I want to avoid memory leaks. I am worried that I miss removing a registered delegate. So I came up with the following construct:
Immediately when registering for an event I also register the command for unregistering to a separate action that I can execute at the end of the program like so:
// first establish a delegate to collect everything we need to de-register later OnDestroy
private delegate void Dreg();
private Dreg dreg;
// register for the event and create an entry in the de-register list
//for later execution:
MyEvents.SomeEvent += HandleSomeEvent;
dreg += () => { MyEvents.SomeEvent -= HandleSomeEvent; };
At the end of the application I just need to invoke "dreg" to clear all registrations. This si working fine so far.
My question is: Can I wrap these two statements more elegantly into a function so that I just have to issue one statement instead of two? I have tried but I fail in the assignments.
My experiment looked something like this:
public static void RegisterForEvent(UnityAction _eventToRegisterFor, UnityAction _thingToAdd)
{
_eventToRegisterFor += _thingToAdd;
dreg += () => _eventToRegisterFor -= _delegateToAdd;
}
...
...Register(MyEvents.SomeEvent,HandleSomeEvent);// using this to register
...
...dreg.invoke;//near the end of the application to de-register everything
This does not seem to be possible.
My problem seems to be that I don't quite understand, what type "thingToAdd" actually is that I am assigning to MyEvents.SomeEvent? Is that a delegate? Is it a string that is resolved at runtime? is it a function??
I tried to work with C# Actions as well but did not succeed with that either.
Any suggestions will be much appreciated! Many thx before hand :-)
Here is the way I solved this:
//---------------------------------------------------------------------------------------------
public static void RegisterForEvent(UnityAction _eventToRegisterFor, UnityAction _handlerToAdd)
{
_eventToRegisterFor += _handlerToAdd;
dreg += () => _eventToRegisterFor -= _handlerToAdd;
}
//---------------------------------------------------------------------------------------------
//...the two lines above could be replaced with this one line call...
RegisterForEvent(MyEvents.SomeEvent,HandlerForSomeEvent);
//---------------------------------------------------------------------------------------------
//...and at the end the invokation of "dreg" will clear the event delegates
private void OnDestroy()
{
dreg.invoke;//near the end of the application to de-register everything
}
This is a follow-up question to another SO question regarding the use of an async wrapper over an async callback function.
Here is the code as it stands (an excellent solution provided by #Servy):
static Task<ObservableCollection<MyResult>> GetMyDataAsync(Params p)
{
var tcs = new TaskCompletionSource<ObservableCollection<MyResult>>();
DoStuffClass stuff = new DoStuffClass();
stuff.LoadCompleted += (args) => tcs.TrySetResult(args.Result);
stuff.LongDrawOutProcessAsync(p);
return tcs.Task;
}
So, my problem is with the LoadCompleted event; here is the signature:
public event EventHandler<MyArgs> LoadCompleted;
MyArgs contains a property called ResultCollection; however, changing the code like this does not work:
stuff.LoadCompleted += (args) => tcs.TrySetResult(args.ResultCollection);
In fact, I get the error:
'System.EventHandler<MyArgs>' does not take 1 arguments
Which I can see if correct from the signature; so how can I set the LoadCompleted result to the TaskCompletionSource?
EventHandler needs 2 arguments, the first is the instance that raised the event and the second is the event arguments. You need to specify both of them even if you only use one (args).
This should work:
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
This should fix your problem
If you look at EventHandler<T> definition you will see it takes two arguments
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
So you need to pass two arguments in your assignment
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
If I have a type that inherits from EventArgs (lets call it EventArgs1), and a further bunch of classes that inherit from EventArgs1 (lets call them collectively EventArgsX), and then a bunch of events that are are of the type EventHandler<EventArgsX>, if at runtime I am passed the EventInfo for one of these events and I want to add an event handler that expects a second argument of type EventArgs1 (e.g. MyEventHandler(object sender, EventArgs1 e)) how would I do it?
If the event was of type EventHandler<EventArgs1> then I would just do this:
eventInfo.AddEventHandler(this, new EventHandler<EventArgs1>(MyEventHandler));
But this throws an exception when the event is of type EventHandler<EventArgsX>, and since I don't know what EventArgsX is at compile time I can't simply new up an EventHandler<EventArgsX> If I did know which event I was adding the handler to at compile time then this would be entirely acceptable:
MyEvent += MyEventHandler
But I simply can't work out how to do this at runtime. Any suggestions?
I can't simply new up an EventHandler<EventArgsX>
Sure you can, although you need to do it using Delegate.CreateDelegate() and reflection. Assuming MyEventHandler is an instance method on this, you could do it like this:
var eventInfo = …;
EventHandler<EventArgs1> badHandler = MyEventHandler;
var goodHandler = Delegate.CreateDelegate(
eventInfo.EventHandlerType, this, badHandler.Method);
eventInfo.AddEventHandler(this, goodHandler);
UPDATE
I have read the question backwards, essentially; I will leave this answer in case someone else who is trying to do the reverse finds this question.
Basically, you can't do what you're trying to do. An event can't add a delegate if the delegate's EventArgs type is a subclass of the event's declared type. You're trying to use covariance when the input parameter is contravariant.
Assume these classes
class EventArgs1 : EventArgs {}
class EventArgsA : EventArgs1 {}
class EventArgsB : EventArgs1 {}
Consider what would happen if you could add a method void Handle(object sender, EventArgsA args) to an event with the signature void SomeEvent(object sender, EventArgs1 args). The event source might do this:
if (SomeEvent != null)
{
var args = new EventArgsB();
SomeEvent(this, args); //this line is perfectly legal, as EventArgsB inherits from EventArgs1.
}
However, when the runtime gets to this delegate in the event's invocation list, it now has to pass the args object to Handle(object, EventArgsA), which it of course can't do, since an EventArgsB instance is not an EventArgsA instance. A run-time exception would result. In reality, the mismatch is caught when you add the delegate, so the exception is thrown then.
What's the benefits of registering an event as:
void MyMethod()
{
button1.Click += delegate (object sender, EventArgs e)
{
..
}
}
in comparison with:
void MyMethod()
{
button1.Click += new System.EventHandler(this.button1_Click);
}
void button1_Click(object sender, EventArgs e)
{
..
}
UPDATE:
And what about unsubscribing it?
The benefit is that you don't have to come up with a name and a place in your class.
For a light function, tightly coupled to the code that register the event, the short version is more convenient.
Note that you can also exchange delegate for a =>
button1.Click += (object sender, EventArgs e) =>
{
..
}
You can be even more concise:
button1.Click += ( sender, e ) =>
{
};
Syntactically it's cleaner (as long as it doesn't lead to long blocks of code which would be better broken up into named methods).
The inline declaration is a closure, which gives you access to the variables adjacent to the anonymous method.
From: What are 'closures' in .NET?
In essence, a closure is a block of code which can be executed at a
later time, but which maintains the environment in which it was first
created - i.e. it can still use the local variables etc of the method
which created it, even after that method has finished executing.
See also: http://csharpindepth.com/articles/chapter5/closures.aspx
When registering event handler with an anonymous delegate or lambda, you can write shorter code and use closures. But you cannot unsubscribe from the event, and if the event handler code is too long, your code becomes ugly.
It's just about coding style.
Worth mantioning that declaring it like in first case let you to avoid "spaghetti code" and inject into the handler function local variables (if needed) in more natural(human readable way).
To be more clear. By writing like in first case you can:
int x = 0;
System.Windows.Forms.Button button = new System.Windows.Forms.Button();
button.Click += (o,e)=> {
++x;
};
Inside the event handler you can access the local variable declared actually out for the scope of the handler method. For most people it seems "natural", but if you think like a developer it shouldn't be even possible.
Good luck.
I'm working with AssemblyResolve specifically. Here is my code first, then my question follows:
var a = AppDomain.CurrentDomain;
a.AssemblyResolve += new ResolveEventHandler(HandleIt);
Private Assembly HandleIt(object sender, ResolveEventArgs args){
//Does stuff, returns an assembly
}
So I add HandleIt to my AssemblyResolve event. How can I add it to that event and pass an argument with it like:
a.AssemblyResolve += new ResolveEventHandler(HandleIt(AnArgument));
This is throwing me off since HandleIt takes arguments, but none are explicetly passed when it is added to the AssemblyResolve event. I would expect something like:
a.AssemblyResolve += new ResolveEventHandler(HandleIt(aSender,someArgs));
So yeah, I just want to be able to send another argument to my HandleIt function when adding it to my AssemblyResolve event.
Hope that makes sense, thanks.
Addendum:
if(aBool){
a.AssemblyResolve += new ResolveEventHandler(HandleA);
}
else{
a.AssemblyResolve += new ResolveEventHandler(HandleB);
}
HandleA(object sender, ResolveEventArgs args){
Handle(true);
}
HandleB(object sender, ResolveEventArgs args){
Handle(false);
}
Handle(bool isA){
}
-vs-
if(aBool){
a.AssemblyResolve += (object s, ResolveEventArgs a) => Handle(s,a,true);
}
else{
a.AssemblyResolve += (object s, ResolveEventArgs a) => Handle(s,a,false);
}
Handle(object sender, ResolveEventArgs args, bool isA){
}
When the event is fired arguments are passed to the method if you would like to bind additional arguments you could do that with a lambdaexpression
var a = AppDomain.CurrentDomain;
a.AssemblyResolve += (object s,ResolveEventArgs a) => HandleIt(s,a,someArgument);
Private Assembly HandleIt(object sender, ResolveEventArgs args, SomeType arg){
//Does stuff, returns an assembly
}
where someArgument is the value you wish to bind.
This is essentially using lambdas to do a partial application of a function. Something that C# doesn't support directly but is very common in other languages. Partial application is very closely related to Currying that exists in languages such as F# and Haskell of course (since the concept gets it's name from Haskell Curry) and various other functional langauages. They differ in the result type.
They are both related to closures (as the concept in the above code is called) and in languages that don't support partial application or currying you can use closures to accomplish something similar. However be aware that closures differ from partial application in ways that can create some surprising bugs. E.g.
int i = 1;
Func<int> f = () => i;
i = 2;
System.Console.WriteLine(f());
prints 2 to the console. Because closures captures a reference to a variable not the value of said variable. This is a common error in for loops, when closing over the loop variable(s) of the for loop.
AppDomain.CurrentDomain will raise the event and pass parameters, the += line is simply registering a handler to the event, parameters there would not make any sense.
Analogy:
You register your address with the postman, and he deilivers mail to that address later. When you register at the post office, you do not hand them the mail you want delivered to you later!
It's not possible.
HandleIt in this case is a delegate to and has to match the signature of ResolveEventHandler
a.AssemblyResolve += new ResolveEventHandler(HandleIt);
Setting it in this line just tells the code what to execute when AssemblyResolve is raised the thing that raises the AssemblyResolved event will pass it's parameters. You could re-raise another event with your own parameters and hook that to another handler (or just call a method).
EDIT: Or maybe you can with Lamda's :o
A bit more technical: You are registering a delegate to your method with the event. This is something else than a call to the method.
It is a bit similar to this:
Action<object, ResolveEventArgs> handleItDelegate = HandleIt;
When the event is fired, the delegate is invoked. The analogy of my sample would be:
handleItDelegate(sender, eventArgs);
UPDATE:
In a comment, you clarified, what you want to achieve: You want to trigger the event, so your assembly is loaded. You can't do that, the way you think it works. To load your assembly manually, just do so, no need for the event. The event is called, when the runtime tries to resolve a referenced assembly, but can't find it.