CamelCase vs underscores: Revisited

It has been 2 years since I published “CamelCase vs underscores: Scientific showdown”, and it still is easily the most visited article on this blog. Yesterday alone it got 2,614 views thanks to a forum post on Y Combinator, pretty much suppressing my normal visit rates entirely. What is it that makes it such a hot topic? Honestly, it doesn’t interest me that much anymore since there are many more important ways by which to make your code more readable; note it is code comprehension we are talking about here, not how fast you can write code! Before I outlined how the entire discussion could be made obsolete by moving away from a textual representation of code, and in my previous post I related software design principles as an act of communication to the cooperative principle in Linguistics. Nonetheless, given the immense interest this article seems to be getting I feel it’s my obligation to report on follow-up research of the previously discussed paper “To camelcase or under_score” by Binkley et al. (2009) (PDF).

In “An Eye Tracking Study on camelCase and under_score Identifier Syles” by Sharif and Maletic (2010) (PDF) the previous study is replicated but deviates from it in a few points:

  • Only programmers are used as subjects.
  • All of the subjects had experience with both styles and their preference of style was approximately split even among the groups.
  • Most of the subjects were historically trained in the underscore style. (The opposite was true in the study by Binkley et al.)
  • Eye tracking is used to measure fixation count and rate. Results from previous eye tracking studies in the domain of cognitive psychology imply that camel-cased identifiers should be more difficult to read compared to underscored identifiers.

No difference in accuracy was reported (as opposed to Binkley et al.), but on average, camel-cased identifiers took 932ms (20%) longer than underscored identifiers, in line with the 13,5% longer as reported by Binkley et al. The eye tracking results also give some insight into visual effort. Camel-cased identifiers require a higher average duration of fixations.

When interested into the details of the studies, don’t forget to read the papers yourself. I linked to them for your convenience, but if the links break you can easily find them by looking up their titles on Google Scholar.

It seems in general the subject has gotten more attention over the past 2 years in research. You can find relevant resources yourself by checking out the ‘Citing Documents’ of the discussed papers, but here are a few interesting ones:

1 Comment

Learning from Linguistics

Don’t worry about the title, this is a post about software design. This week I started following a Coursera course, Think Again: How to Reason and Argue. In it, the cooperative principle and its constituting maxims are presented which describe how people ideally should interact with each other. I realized the maxims apply to software design as well; after all, programming is a cooperative process, at least if you want your code to be usable by others. At the very minimum all of the maxims apply to the comments and documentation, them being language. Sometimes a direct relation to the existing design principles we follow can be drawn.

Maxim of Quantity

  1. Make your contribution as informative as is required (for the current
    purposes of the exchange).
  2. Do not make your contribution more informative than is required.

Point 1 boils down to stating everything relevant to the current situation without leaving out any important details.

public class Machine
{
	bool _enabled;
	public void Enable() { _enabled = true; }
	public void Disable() { _enabled = false; }
}

This code leaves out important information, namely the state of the machine. This should be publicly visible.

public class Machine
{
	public bool Enabled { get; private set; }
	public void Enable() { _enabled = true; }
	public void Disable() { _enabled = false; }
}

Point 2 states you should only show the relevant information. This reflects the concept of information hiding which states internal logic should be encapsulated and only a stable interface should be exposed.

Maxim of Quality

  1. Do not say what you believe to be false.
  2. Do not say that for which you lack adequate evidence.

Point 1 simply states, “do not lie”. Now you might wonder how to lie when programming, but the following is an all too common example.

try
{
	DangerousOperation();
}
catch ( Exception ) {}

Obviously this exception hasn’t been handled. Testing your software gives you the necessary evidence that your code works for the provided use cases, addressing point 2.

Maxim of Relation

  • Be relevant!

Within linguistics, being irrelevant can for example mean changing the subject when answering a question. Although possibly a long stretch, the Seperation of Concerns principle states a computer program should be separated into distinct features overlapping as little as possible. The notion of cohesion refers to the degree to which elements of a module belong together. High cohesion relates to being relevant, letting a module only perform those tasks related to each other.

Maxim of Manner

  1. Avoid obscurity of expression.
  2. Avoid ambiguity.
  3. Be brief.
  4. Be orderly.

Being brief means conveying something as concise as possible. Well, for starters don’t repeat yourself! Your interfaces/APIs should be unambiguous and as clear as possible. There are many examples of this, but here’s two.

class Controller { ... }        // Obscure.
public int GetWeight() { ... }  // Ambiguous.

Lastly, be orderly. Don’t place everything in one namespace, and code consistently. Follow the conventions in your current work environment, as this will lead to the least surprises.

Programming is very much a linguistic act and thus we could probably learn a thing or two from linguistics and social sciences in general.

Programs must be written for people to read, and only incidentally for machines to execute. - Abelson & Sussman, Structure and Interpretation of Computer Programs

2 Comments

Rethinking tabs: scalable window management


Tabbed Document Interfaces
(TDIs) have become mainstream nowadays. Did you ever stop and wonder why? Which problem has been solved by introducing them? In this post I will shortly take you through their history, explain why I feel tabs don’t solve the fundamental problem, and why Activity-Based Computing (ABC) might be a more appropriate solution.

History

Tabs aren’t as recent as most people think. They gained mainstream success once tabbed browsing was introduced (and no, Opera wasn’t the first web browser to do so), but early environments already supported them. Smalltalk (from Xerox PARC) was a programming language, but its windowed GUI is what eventually inspired windowing environments for personal computers.

Windows in Smalltalk

Although not exactly tabs as we know them today, I found it important to show what most likely has been the inspiration for them. Since the tabs are always positioned at the top left of the window, you can imagine quickly running out of horizontal screen space if you were to outline them next to each other. In this sense you can argue these aren’t tabs at all, but just plain windows. This changed when UniPress’s Gosling Emacs moved the tab to the right hand side of the window. Since tabs take up a lot less space vertically, outlining them underneath each other doesn’t take up as much space as outlining them horizontally.

UniPress Gosling Emacs

Don Hopkins presents having more than 10 windows open under HyperTIES, which is an Emacs based authoring tool. The tabs might not behave as they do today, but they solve a similar problem: allowing to have many documents open simultaneously and easily switching between them.

The problem

Having many documents open and switching between them is the task of a window manager, not specific applications. The definition of a window manager according to dictionary.com is:

A part of a window system which arranges windows on a screen. It is responsible for moving and resizing windows, and other such functions common to all applications.

When an application lets the operating system’s window manager handle all its individual windows separately, it has a single document interface (SDI). This approach quickly resulted in cluttered taskbars, which is why multiple document interfaces (MDIs) and tabbed document interfaces (TDIs) became popular. Limitations of the window manager forced applications to address problems which should have been addressed by window managers in the first place. Consequently, many applications are reinventing the wheel.

Remember that taskbar at the bottom of your screen (usually)? Remember what it used to do before it got all modernized and fancy new features were added? When you’d maximize all of your windows you basically had a tabbed interface with the tabs at the bottom. To exemplify I turned off some of the features in my current Windows 7 setup in order to make it resemble what it looked like in Windows 95, as also visualized in the website I have open.

Taskbars

All TDIs and MDIs are doing is moving the problem to a different location. They hide the clutter, only for it to reappear once the application has to manage many open documents itself. This can be useful when the open documents are related to each other, as is often the case in an integrated development environment (IDE), but this definitely isn’t the case for every application. Ironically web browsers are a prime example of applications which often manage unrelated open ‘documents’.

Imagine a desktop where every application decides for itself where to place the ‘close’ button or how to resize it’s window. The exact same thing is currently going on with document management.

Existing solutions

The problem should be handled by the window manager. Applications don’t need to re-implement the same functionality over and over again. This is in line with a principle in software development called ‘Don’t Repeat Yourself’ (DRY) which I hold in high regard. Additionally the same look and feel can be provided across applications. Some steps have been taken towards this end, of which I’ll discuss a few here.

The windows 7 taskbar allows you to group windows together based on the application they belong to. In the screenshot below explorer windows are grouped together in one icon on the taskbar. Usually you know in which application you have a certain document open, so you can easily find it by performing just one extra step.

Windows 7 Taskbar

The problem I see here is the same as mentioned earlier: this simply moves the problem to a different location, and again, documents open in one application aren’t necessarily related to each other contextually. Additionally, even when the taskbar isn’t full, you still need to go through this extra step. (Note: you can configure to only group windows when the taskbar is full.)

Some modern window managers allow you to group windows together manually.

Bumptop

Allowing you to manually organize windows at least gives you the flexibility to create your own context as you see fit, but unfortunately again these approaches don’t scale well.

… which of course begs the question, do they have to scale that well?

I prefer turning this question on its head and looking at it from an opportunistic angle.

Scaling window management

What if window management would be more scalable?

First, some food for thought. Yes, more questions …

  • What is your main reason for closing a window?
  • Why do you organize files?
  • What is your main reason for not deleting a file?
  • In what way do you organize windows?
  • How does file management relate to window management?

A lot of effort goes into setting up the working environment for a particular activity. Wouldn’t it be nice if you were able to ‘store’ this setup and retrieve (or even reuse) it at a later time, just as you are able to store (and reuse) files?

Perhaps the real problem isn’t how we access windows, but how we store them. In order to store and organize these different configurations of windows I proposed using a time line in my thesis.

Laevo Time Line

This is inspired by Activity-Based Computing (ABC) which I wrote about previously, which states that activity should be the central computational unit instead of files and applications. In the context of window management activity can be seen as all the windows needed to achieve a certain goal. It’s up to you to decide what makes up a relevant context, organizing work as you like.

Taking this to the extreme: assume closing a window would be the same as deleting a file. Would you actually ever have to know about the underlying file system again? Window management and file management could become one and the same thing.

1 Comment

Activity-Based Computing

As I’ve mentioned before, I’ve been quite busy, hence the lack of posts over the past few months. A big portion of my time went into developing a prototype user interface for my thesis titled “Managing Activities in Time and Space in an Activity-Based Computing System”. No worries if you don’t have a clue what that means, as I’ll try to do my best to explain the concept in at least 80 pages less than my thesis. In future posts I will disclose the actual prototype and will work towards a stable beta release. Until then – for the curious – you can already access the open source project, but I still need to add instructions on how to compile it in the readme.

It came to me as a surprise, that the very same idea I had for my thesis was already being discussed in the 80s, in the early days of Human-Computer Interaction (HCI) over at Xerox PARC. It came to me as a bigger surprise that even though several research departments have been researching the subject over a course of more than 30 years (including a research group over at Apple Computer), the concept still hasn’t caught on in mainstream computing. Activity-Based Computing (ABC) is a new computing paradigm which attempts to address some of the problems with the current paradigm.

Applications: what a terrible term. What a terrible concept. Applications have little to do with the tasks that people are attempting to accomplish. Look. We don’t do word processing; we write letters, or memos, or reports, or notes to ourselves. - Donald A. Norman

Computers are inherently application and document oriented. This is all good and well for simple tasks, such as using a text editor to write a certain document, but is that still a realistic scenario? Computers nowadays make up a big portion of our daily lives. We don’t just write a paper; we look up information on the internet, translate words, use a dictionary, email with contacts for further information, import images, keep track of the deadline when the work needs to be finished, share a draft version, wait for feedback, and later have to reopen our entire working context again to make amendments. A lot of tasks users need to carry out require the use of several applications and documents, simultaneously. Furthermore, we don’t work on just one task at a time. Interruptions can cause us to suspend work on one task and open up other documents/applications required to continue work on another.

Gaaah! I believe this portrays the problem pretty well. Most likely your desktop never looks this bad, but that’s because you tend to close windows you believe you no longer need, exactly to avoid this kind of situation. But at what cost? Think back for a second how often the following thought went through your head: “If only I hadn’t closed that before.”. We lose a lot of time re-initializing a working context which we already set up before, be it one hour, one day, or a month ago. But that’s not all. Computing has become ubiquitous. We don’t just use one device, but several. We don’t keep documents to ourselves either, we share them. Managing applications and documents has become a task in it’s own right; a repetitive, distracting, possibly unnecessary task. I hate unnecessary repetition.

Proponents of Activity-Based Computing feel a lot of this overhead can be eliminated by using activity as the basic computational unit, instead of the file or application. Activity, as defined in activity theory, is seen as “a system of human “doing” whereby a subject works on an object in order to obtain a desired outcome.” This takes into account the entire setting in which the activity takes place; the tools used, the people you communicate and share resources with, the rules governing the community and how work on the activity is divided between people. Activity being a computational unit implies you can open, close, save and manage activities, not too dissimilar as to how you use files now. “The end-user is directly supported by computational activities which can be initiated, suspended, stored, resumed, and shared on any computing device in the infrastructure at any point in time, handed over to other persons, or shared among several persons.”

Humans perform plenty of activities, and thus within existing ABC systems the question arised how to manage them. The concept I explored in my thesis was to provide an overview of activities in time and space, showing when work was done on them and their inner relationships. I argue it is a worthwhile path to explore using a calendar not only to plan your activities, but also to use them. My prototype works towards this integration of activities as a computational unit managed on a calendar. Since the start of this month I started work as a research assistant at the IT University of Copenhagen where I will be researching how ABC can be used by software developers, specifically in software companies with distributed teams, following agile software development. Exciting times are ahead!

3 Comments

Conditional project or library reference in Visual Studio

In case you were wondering why you haven’t heard from me in a while, I’ve been busy, which isn’t really of much importance unless you know me on a personal level. What is relevant is that I recently graduated with a master in Game and Media Technology and am now in the process of making the project which I have been working on for my thesis open source. I’m very anxious to announce it, so you’ll read a lot more about it in the near future.

The project uses two of my other open source libraries, located in separate repositories. Adding these projects to the solution and referencing them as project references has the advantage of easier debugging and facilitates making changes to them. However, I do not want to force anyone interested in making changes to the main project to having to download the other repositories as well. Therefore I opted to use DLL references. This has one major downside. Whenever I do make changes to one of the dependent libraries, I need to manually copy the newly compiled DLLs to the main project. Wouldn’t it be easy to use two separate solution files, one with project references, and one with DLL references?

The first problem you’ll encounter is project references aren’t stored in the .sln file but in the .csproj file, which makes sense really. Since the .csproj file is shared by both solutions, we’ll have to conditionally reference either our DLLs or our projects, depending on which solution the project is opened in. This is possible using MSBuild, but you will need to create a new project configuration. Setting the configuration of the project differently in one solution from the other allows you to differentiate between the two of them. The following is part of a .csproj file which conditionally loads a reference by checking whether the project configuration is set to ‘Debug With Project References’.

  <Choose>
    <When Condition="'$(Configuration)' == 'Debug With Project References'">
      <ItemGroup>
        <ProjectReference Include="..\SomeProject\SomeProject.csproj">
          <Project>{6CA7AB2C-2D8D-422A-9FD4-2992BE62720A}</Project>
          <Name>SomeProject</Name>
        </ProjectReference>
      </ItemGroup>
    </When>
    <Otherwise>
      <ItemGroup>
        <Reference Include="SomeProject">
          <HintPath>..\Libraries\SomeProject.dll</HintPath>
        </Reference>
      </ItemGroup>
    </Otherwise>
  </Choose>

You could very well stop here, but I wanted to resolve another issue as well. Unless you follow a really strict folder structure, and force everyone else who wants to open your solution to do the same, the path used to reference the project can vary between people. Remember we are using separate repositories here, and the referenced projects aren’t included in the repository of the main solution, so relative paths don’t necessarily work. It is possible to specify the paths in an external ‘configuration’ file, and importing it into the .csproj file. Additionally, as a fallback when the project can’t be found at the given path, it is also useful to load the DLL instead. This way you can choose to load one or more, but not necessarily all references as a project reference.

The configuration file (ProjectReferences.txt):

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Condition="$(Configuration) == 'Debug With Project References'">
	<SomeProject>..\SomeProject</SomeProject>
  </PropertyGroup>
</Project>

Segment from the .csproj file:

  <Import Project="..\ProjectReferences.txt" />
  <Choose>
    <When Condition="Exists($(SomeProject))">
      <ItemGroup>
        <ProjectReference Include="$(SomeProject)\SomeProject.csproj">
          <Project>{6CA7AB2C-2D8D-422A-9FD4-2992BE62720A}</Project>
          <Name>SomeProject</Name>
        </ProjectReference>
      </ItemGroup>
    </When>
    <Otherwise>
      <ItemGroup>
        <Reference Include="SomeProject">
          <HintPath>..\Libraries\SomeProject.dll</HintPath>
        </Reference>
      </ItemGroup>
    </Otherwise>
  </Choose>

Notice the project configuration check now occurs in the configuration file. The ‘$(SomeProject)’ property is only set when using the ‘Debug With Project References’ configuration, thus in all other scenarios the DLL will be loaded instead since the ‘Exists()‘ check will fail.

One last issue remains. We still need to manually copy the latest compiled DLLs to the ‘Libraries’ folder when changes were made for the solution which uses them to work as expected. We can exploit the fact that we now have the relevant project paths available in the configuration file. Using a post build event you can call a script which parses the XML data, and copies the DLLs to the correct location. This script should be called conditionally, only for the solution which includes the project references, and ideally also only for a Release build. I used a small Ruby script which offers a lot more flexibility than a Batch script.

The post build event:

if "$(SolutionName)" == "SomeSolution With Project References" if "$(ConfigurationName)" == "Release" ruby $(SolutionDir)UpdateLibraryDlls.rb $(SolutionDir)

Leave a Comment

Don’t Repeat Yourself as a Lifestyle

Yesterday I saw a truly inspiring talk by Bret Victor where he explains his guiding principle in life and work.

“Ideas are very important to me. I think that bringing ideas into the world is one of the most important things that people do. [...] Creators need an immediate connection with what they are creating.” – Bret Victor

It’s quite a lengthy talk, but the crux of it doesn’t boil down to just this principle. He goes on to describe why he follows this principle. What motivates him.

“When I see a violation of this principle, I don’t think of that as an opportunity. [...] Ideas are very precious to me, and when I see ideas dying, it hurts; I see a tragedy. To me it feels like a moral wrong, it feels like an injustice, and if I think there is anything I can do about it, I feel it is my responsibility to do so; not opportunity, but responsibility.” – Bret Victor

Although Bret spends a great amount of time demonstrating some very imaginative interfaces, the bottom line of the talk isn’t about him showing of his principle, but meant to inspire you in finding your own, and why technology can play an important role in that.

“As a technologist you can recognize the wrong in the world. You can have a vision for what a better world could be, and you can dedicate yourself to fighting for a principle. Social activists typically fight by organizing, but you can fight by inventing.”

“I’m not saying you that you have to live this way. I’m not saying that you should live this way. What I’m saying is that you can, that this lifestyle is an option that’s available to you.” – Bret Victor

Only this morning I realized what this principle could be for me.

The DRY (Don’t Repeat Yourself) principle states: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. – The Pragmatic Programmer

I’ve always held this fundamental software development principle in high regard, but just as the original intent of the statement wasn’t solely about preventing duplicate code I realize now I assign even more importance to it. For me it transcends software development.

I’m writing this while I’m on the train, on my way to work. I spend quite a lot of time on train travel, having to go from Belgium, to The Netherlands and to England. It’s mind-numbing having to take the same routes over and over again, week after week, but the ability to work or read while I do so helps me cope with that. Doing the same trips by car would drive me insane. It’s only one of the many indications in life which cause me to believe that repetition is a cause of dread.

Although I didn’t interpret it as such before, the past few years it has been my personal goal to attempt eliminating any form of repetition. Everything I feel strongly about revolves around eliminating the need to do the same things over and over again. From changing the way a community works by working towards a more constructive environment in which to have discussions so arguments no longer need to be repeated over and over again, to eliminating the need of ever having to write an unnecessary piece of code again. The thesis I am currently working on is inspired by the fact that people waste too much of their time managing and switching between different activity contexts. It’s repetitive work which could be solved by using an Activity-Based Computing system.

The wonderful thing about technology is that it’s a perfect enabler to attain that goal, but I realize now it is not only what defines me as a developer, but also as a person. Some people call me lazy because I don’t like doing daily chores. They might be right, I rather work on things which prevent me from having to do something ever again. I like my work as much as I do because it’s non-repetitive by nature, unless you end up in a mindless work environment where you are considered to be nothing more than a Code Monkey.

I believe I found my personal guiding principle, what’s yours?

2 Comments

Null Checks for Event Handlers, an Aspect Solution

There is no denying events in C# are a great step forward from more traditional approaches where a lot of boilerplate code needed to be written. I choose C# events over Java’s solution to the Observer pattern any day.

… but, there is one pesky little issue I have with them. By design, it’s really easy to introduce bugs using them. How often have you encountered a NullReferenceException because you didn’t check whether an event handler was null prior to calling it? As a more experienced developer you might choose one out of the following two options.

1. Repeated null checks

Always check whether an event isn’t null prior to calling it. Additionally, you need to copy the event handler to a local variable, otherwise another thread might unsubscribe from the event causing the handler to be null again after you did the null check.

EventHandler handler = SomeEvent;
if ( handler != null )
{
    handler( ... );
}

2. Always subscribe one empty delegate

There might be a small cost of constantly having to call one extra function when the event is raised, but it solves the problem of having to do repeated null checks throughout the code while simultaneously solving the multi-threading issue.

public event EventHandler SomeEvent = delegate {};

The Aspect way

I prefer the second approach, and don’t see any reason why it shouldn’t be used by default, other than a smarter compiler which could take care of this in a ‘cleaner’ manner. That’s why over the weekend I’ve spent some time seeing how PostSharp could help solving this cross-cutting concern. I always love showing off the result prior to explaining how I got there. It’s usage is a one liner.

[assembly: InitializeEventHandlers( AttributeTargetTypes = "Laevo.*" )]
namespace Laevo { ... }

Adding this one attribute prevents you from having to worry about events throughout the entire namespace it applies to.

How does it work?

First of all, you will need PostSharp. That’s not necessarily a bad thing, because if you are fortunate enough to be allowed to use it, or you can simply decide for yourself to buy it, you will greatly appreciate it for more than just this one possible use case.

The InitializeEventHandlersAttribute is called an aspect which is applied on events. However, using a feature of PostSharp called ‘multicasting’, you are able to apply the attribute (MulticastAttribute) on higher level elements, allowing you to choose which one of the lower level elements the aspect should apply to. In the code above, the aspect is applied on an entire assembly, but the aspect knows it should only be applied to events, which is further narrowed down using the AttributeTargetTypes parameter to events within the namespace “Laevo”, and all sub-namespaces. In order to remind the developer that a certain part of his code is enhanced by an aspect, a visualization as follows is visible within Visual Studio.

The code is short enough to paste in its entirety now, but I’ll decompose it in a minute.

[AttributeUsage( AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Event )]
[MulticastAttributeUsage( MulticastTargets.Event, AllowMultiple = false )]
[AspectTypeDependency(
    AspectDependencyAction.Commute,
    typeof( InitializeEventHandlersAttribute ) )]
[Serializable]
public class InitializeEventHandlersAttribute : EventLevelAspect
{
	[NonSerialized]
	Action<object> _addEmptyEventHandler;

	[OnMethodEntryAdvice, MethodPointcut( "SelectConstructors" )]
	public void OnConstructorEntry( MethodExecutionArgs args )
	{
		_addEmptyEventHandler( args.Instance );
	}

	IEnumerable<ConstructorInfo> SelectConstructors( EventInfo target )
	{
		return target.DeclaringType.GetConstructors(
                    BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
	}

	public override void RuntimeInitialize( EventInfo eventInfo )
	{
		base.RuntimeInitialize( eventInfo );

		// Construct a suitable empty event handler.
		MethodInfo delegateInfo = DelegateHelper.MethodInfoFromDelegateType(
                    eventInfo.EventHandlerType );
		ParameterExpression[] parameters = delegateInfo.GetParameters().Select( p =>
                    Expression.Parameter( p.ParameterType ) ).ToArray();
		Delegate emptyDelegate = Expression.Lambda(
                    eventInfo.EventHandlerType, Expression.Empty(),
                    "EmptyDelegate", true, parameters ).Compile();

		// Create a delegate which adds the empty handler to an instance.
		_addEmptyEventHandler = instance => eventInfo.AddEventHandler( instance, emptyDelegate );
	}
}

The steps I followed to create this aspect are documented in the PostSharp 2.1 Documentation “Adding Behaviors to Members”.

  1. Create a new class, deriving from the relevant aspect class. Since I am creating an aspect which is applied on events, I used EventLevelAspect.
  2. Choose on which part of the code your aspect will have effect. This is called an ‘advice’. Although my aspect is applied on events, the solution I used is to add an empty event handler to the events by default. This is something which can be done from the constructor. Therefore I had to use an OnMethodEntryAdvice in order to add this code to the beginning of every constructor.
[OnMethodEntryAdvice, MethodPointcut( "SelectConstructors" )]
public void OnConstructorEntry( MethodExecutionArgs args )
{
	_addEmptyEventHandler( args.Instance );
}
  1. Choosing a ‘pointcut’ is required to determine where the code needs to be added. We’ve already established it needs to be added to the start of methods by using OnMethodEntryAdvice, but we haven’t established which methods it needs to be applied to yet. By using a MethodPointcut, this task is delegated to a separate method of which you pass the name. In my case, this method doesn’t do much more than returning all the constructors of the current type.
IEnumerable<ConstructorInfo> SelectConstructors( EventInfo target )
{
	return target.DeclaringType.GetConstructors(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
}
  1. We are nearly done, although there is one part of the code I skipped over which is quite interesting nonetheless. The OnMethodEntryAdvice calls the delegate _addEmptyEventHandler(), but where did this come from? Overriding RuntimeInitialize gives you the chance to initialize objects which the advice will require for the specific element the aspect applies to. Using reflection to analyze the passed EventInfo object, a suitable empty event handler can be constructed using Expression Trees. This is required since we don’t have the luxury of using an equivalent to ‘delegate {}’. Finally a simple delegate is created which can add the empty delegate to the event of any given instance.
public override void RuntimeInitialize( EventInfo eventInfo )
{
	base.RuntimeInitialize( eventInfo );

	// Construct a suitable empty event handler.
	MethodInfo delegateInfo = DelegateHelper.MethodInfoFromDelegateType( eventInfo.EventHandlerType );
	ParameterExpression[] parameters = delegateInfo.GetParameters().Select( p => Expression.Parameter( p.ParameterType ) ).ToArray();
	Delegate emptyDelegate
		= Expression.Lambda( eventInfo.EventHandlerType, Expression.Empty(), "EmptyDelegate", true, parameters ).Compile();

	// Create a delegate which adds the empty handler to an instance.
	_addEmptyEventHandler = instance => eventInfo.AddEventHandler( instance, emptyDelegate );
}

… and all that just to eliminate ‘= delegate {}’? Yes, I can be quite stubborn sometimes. :)

UPDATE:

This code has since been slightly altered in order to also support events with generic parameters within generic types. Thank you Anupheaus, for reporting this issue! Be sure to check out the latest code on github. A discussion of the problem, and how to work around it can be found in my answer on Stack Overflow.

9 Comments

Follow

Get every new post delivered to your Inbox.