A place for spare thoughts

27/02/2012

Weak delegates for non-standard event handlers (e.g. WPF)

Filed under: wpf — Ivan Danilov @ 14:04

About Weak delegates there’s many articles lurking around the Internet. The most beautiful IMO is this one. So, I assume that you already read it and won’t repeat that info.

So, some time ago (pretty much time, actually, just hadn’t time to write earlier), inspired by the beauty of the article I happily copied MakeWeak extension method to our codebase… just to find out that the most frequently used WPF event is declared as

event PropertyChangedEventHandler PropertyChanged;

public delegate void PropertyChangedEventHandler(object sender,
                                                 PropertyChangedEventArgs e);

Remember Dustin’s complaints about MS not following their own recommendations? That’s it. Problem. Why is it not declared as that?

event EventHandler<PropertyChangedEventArgs> PropertyChanged;

Who knows? I’m interested too. Can’t see any problems and one type to load less for CLR. But anyway, if I want to work with WPF – I should invent something about the problem.

And so the saga begins… oops, it is not from this story, sorry πŸ™‚

So, lets define first what our weak events MUST HAVE (i.e. without what they wouldn’t be useful at all). For me these requirements include at least:

  • It should be weak and should be working! (surprise, eh?)
  • I shouldn’t specify type parameters explicitly, they should be fully inferred from usage;
  • I want static check that actual unsubscription is correctly typed and I don’t unsubscribe delegate of wrong type (so, basically I want be at least minimally defended against InvalidCaseException at runtime);
  • Performance should be at least the same order as with normal delegates, i.e. no hardcore reflection on every event invocation.

The very first problem is to obtain original event actually. Originally MakeWeak defined as

public static EventHandler<TEventArgs> MakeWeak<TEventArgs>(
        this EventHandler<TEventArgs> eventHandler,
        UnregisterCallback<TEventArgs> unregister)
    where TEventArgs : EventArgs

See the problem? With non-standard delegates we don’t have EventHandler<TArgs>. And we have to save that delegate later after ‘casting’ it to OpenEventHandler in the WeakEventHandler class – there we need MethodInfo instance that originally obtained via eventHandler.Method property. So the idea – lets parametrize this method with one yet type parameter, like that:

public static EventHandler<TEventArgs> MyMakeWeak<THandler, TEventArgs>(
        this EventHandler<TEventArgs> eventHandler,
        UnregisterCallback<TEventArgs> unregister)
    where THandler : Delegate
    where TEventArgs : EventArgs

So we pass eventHandler and Delegate has Method property as well, so it should be fine… nope, it’s not. If you try to compile that you have “Constraint cannot be special class ‘System.Delegate'” from compiler. Eric Lippert said that there’s no technical reason to not support such syntax, but it is just not needed often, so it is pretty low on priority list. No luck! I have to thought out something else.

OK, let’s sacrifice type safety here and make extension method accept any type T:

TEventHandler MyMakeWeakTEventHandler>(this TEventHandler eventHandler, 
                                       Action<TEventHandler> unregister)

It is not a big deal and doesn’t break our requirement – remember, we will use this method only with that syntax:

obj.PropertyChanged += new PropertyChangedEventHandler(MyMethod)
    .MyMakeWeak(h => obj.PropertyChanged -= h);

In other words, if we try to pass not PropertyChangedEventHandler but some wrong class there, it will violate types at += operator because we’re returning exactly that type that was passed to our extension method.

So, what will we have then? We can rely on the fact that TEventHandler in reality is derived from Delegate class and can be cast to it. Yes, it is possible to misuse that method and get exception at runtime only, but it is hard to do unintentionally, so I’m fine with that.

The next problem: we don’t know argument type from the start. Before it was TEventArgs : EventArgs, but now we don’t have such thing. Well, if we can cast our param to Delegate – we can know list of its parameters via Delegate.Method.GetParameters()[index].ParameterType. Not very pretty and we don’t know concrete signature of the delegate – even number of parameters… Wait, we do know number of parameters almost always – fortunately, WPF team didn’t abandon guidelines totally and their delegates have normal void (object sender, TEventArgs e) signature everywhere. That’s something. Yep, again, it won’t work absolutely everywhere, but in worst case I can extend this approach in the same ugly way as Func can have various number of type arguments (no, I will not do this here because I don’t need this – at least I haven’t so far).

So, MyWeakEventHandler will have one generic type parameter more as it needs to know not only receiver type and event args type, but exact delegate type as well.

So far, so good. It seems everything is pretty much clear with MyMakeWeak method – at least if it will work in the end (remember first requirement?). And one last stroke: instead of strange MyMakeWeak I finally named it MakeWeakSpecial. While I could name it MakeWeak too as original one (which stays untouched) it would bring to scene C#’s overload resolution rules which are insanely complex and sometimes very confusing in the presence of generics. So I’ve decided to name it differently just to be sure what I’m using. Here’s what we have:

public static TEventHandler MakeWeakSpecial<TEventHandler>(this TEventHandler eventHandler, Action<TEventHandler> unregister)
{
    if (eventHandler == null)
        throw new ArgumentNullException("eventHandler");

    var ehDelegate = (Delegate) (object) eventHandler;
    var eventArgsType = ehDelegate.Method.GetParameters()[1].ParameterType;
    Type wehType = typeof(WeakEventHandlerSpecial<,,>)
        .MakeGenericType(ehDelegate.Method.DeclaringType, 
                         typeof(TEventHandler), 
                         eventArgsType);

    ConstructorInfo wehConstructor = wehType.GetConstructor(new[]
                                                            {
                                                                typeof(Delegate), 
                                                                typeof(Action<object>) 
                                                            });

    Debug.Assert(wehConstructor != null, "Something went wrong. There should be constructor with these types");

    var weh = (IWeakEventHandlerSpecial<TEventHandler>)wehConstructor
        .Invoke(new object[] 
                {
                    eventHandler, 
                    (Action<object>)(o => unregister((TEventHandler) o)) 
                });

    return weh.Handler;
}

Well, lets go to WeakEventHandlerSpecial<T, TEventHandler, TEventArgs>. The only problem we have there is that TEventHandler has no relation to TEventArgs. So, original concise _handler = Invoke; is not applicable, as _handler has type TEventHandler and Invoke is method group with (object, TEventArgs) signature. It is not hard problem: we just need to obtain current instance’s type, find its method "Invoke" and dynamically create delegate of type TEventHandler with Delegate.CreateDelegate. And what about performance, you could ask? Well, it is another costly operation. But we’re still in the code region that will be executed once for a subscription, NOT once per event occurring. So I deem it reasonable (Again, in the worst case if I need to optimize these at some point – nobody prevents me from some caching strategy or creating compiled Expression or any other mechanism – thus, from ‘once per subscription’ it will be ‘once per given three types’).

And so, finally, here you can get results (WeakDelegates.zip) with several unit tests: link.

And in case you don’t want to download something and want just a quick-look at what I have as result – you have this gist.

And regarding requirements:

1. It’s working. Unit tests are swearing that it is so.
2. While using I’m not forced to specify anything.
3. In the only real-world usage types are checked. Other cases are not, but it is ok.
4. There’s somewhat more reflection than it was in the original weak events, but only slightly.

I think my own requirements are pretty much fulfilled. πŸ™‚

Advertisements

22/02/2012

Visual Studio usability gem – Perspectives extension

Filed under: VisualStudio — Ivan Danilov @ 03:31

Back in VS 6.0 days there was a feature to manage environments of the Studio: what tool windows you have opened, their layout, toolbar configuration and position etc. It was very handy because when you write code – you don’t need bunch of windows dedicated to debug process like threads, watches, call stacks and the like. After VS 6.0 this feature was silently buried. Instead what you have now is just two configurations of layout: one for debug and one for non-debug times. And VS switches them itself without your intervention.

While these two could be enough sometimes, it is not always the case apparently. In the multi-monitor days it becomes even more true. If you’re actively writing code and looking at connected parts of codebase constantly – you want as much space for your editor windows as possible. Even on several monitors at once. If you have MSDN or stackoverflow opened in your browser and you’re trying to figure out how could they be applied to your situation – you want probably to see browser on the second screen, don’t you? But VS’ toolwindow will readily go on top of the browser instantly as you activate IDE. Arghhh! What can you do? Close toolwindow or dock it somewhere else (just to return it back manually when you no longer need browser). And there are several such scenarios that are forcing to shuffle windows forth and back constantly.

Today I found Perspectives. Basically it is what VS 6.0 had a decade ago – layout manager. It is somewhat buggy, but still can apply and save layouts wonderfully. With that functionality I definitely can tolerate several minor UI issues.

21/02/2012

MSTest 2010 without Visual Studio at build server – again

Filed under: MSBuild, Unit testing — Ivan Danilov @ 02:53

A while back I wrote on that topic. Today I found that that solution was not working when MSTest private accessors were used in the solution.

In general I think that these ability to access private members of the class encourages bad design and nullifies one of the most useful features of testing: necessity to think about how your class can be used and how convenient its interface is. If you have ability to work with privates – you don’t have to think. You could just write code as it goes. How are consumers of the class supposed to use it when even its author can’t?!
Thus, IMO, ability to generate private accessor to a class is one of the most abominable things in testing and I’ll never ever use it.

But unfortunately others do sometimes. If you have such a project and try to build it on a machine that was setup using my previous advice – you’ll see something about compiler can’t find out meaning of name YourClass_Accessor. Of course it can’t – it wasn’t generated yet! To overcome this issue you have to copy in addition to previous files also this folder:

c:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\TeamTest\

It contains *.targets file that was foresightfully imported even in default Microsoft.Common.Targets (even if you will never have MSTest – you still have line to import it if it is found), one assembly with custom MSBuild Task implementation and some exe-module. For some reason, MSTest developers can’t manage to write a product without such hacks at every corner. Whatever.

So, you just copied these files and msbuild will find them. Do not even hope to have such a luxury for your products πŸ™‚
But when I rerun the build – I found such error:

C:\Program Files\MSBuild\Microsoft\VisualStudio\v10.0\TeamTest\Microsoft.TeamTest.targets(14,5): error : Could not load file or assembly β€˜file:///C:\work\TestSolution\TestProject\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll’ or one of its dependencies. The system cannot find the file specified.

What?! Why the hell MSTest tries to load an assembly from current directory and not with normal binding mechanism? I have this assembly on my build machine and it actually can be loaded when referenced from project in normal way. I didn’t found answer to that question as well as option to alter this probe somehow. But with fusionlog I found that just after probe to current dir it tries to load same assembly from its default location at c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll. Ok. Let’s stick to it. And I’ve copied all of my assemblies not to c:\vs2010stub as I suggested in previous article, but to default c:\Program Files (x86)\Microsoft Visual Studio 10.0\ folder. And it worked – private accessors got generated and publicized correctly.

So I made related changes to my archive and re-uploaded it to old location. Just unpack somewhere on build server and run install.bat. Should work afterwards.

Create a free website or blog at WordPress.com.