September 2010 - Posts

Extension Method Guidelines Rebuffed

With the additional of extension methods developers have a powerful new way of extending existing types.  As extension methods become more and more common guidelines will be needed to ensure that extension methods "fit in" with the general design of the library.  If you are not aware of what an extension method is then the quick summary would be that it is a method that appears as an instance method of a type but is, in fact, defined in a separate static class.  This allows us to extend existing types without modifying the type itself.  It really becomes a powerful feature when applied to interfaces.  We can enhance an interface to expose a lot of new functionality while not requiring the interface implementer to do any extra work.  LINQ is a good example of this extensibility.  I personally tend to view extension methods more as static methods on a type  that be called using the instance-method syntax.  This, to me, gives a more accurate picture of what is happening and, as you will see, what is allowed.

Currently there are a lot of "guidelines" floating around that people like to mention.  Unfortunately it is still too early to determine which of these are truly best practices and which are opinions.  One guideline in particular bothers me the wrong way: extension methods should throw a NullReferenceException if the source is null.

Let's start with a regular method as an example.  This method will return everything to the left of a particular string in another string.

static string Leftof ( string source, string value )
{
   if (String.IsNullOrEmpty(source))
      return "";

   if (String.IsNullOrEmpty(value))
      return "";
   ...
}

This is a pretty handy function so it makes sense to make it an extension method so we might do this.

static class StringExtension
{
   public static string Leftof ( this string source, string value )
   {
      if (String.IsNullOrEmpty(source))
         return "";

      if (String.IsNullOrEmpty(value))
         return "";
      ...
   }
}

So far so good but it violates the aforementioned rule about throwing an exception if a null is passed in.  The rationale is that an extension method appears to the developer as an instance method and should therefore behave like one.  While that is a reasonable argument I don't think it applies in all cases.  Why do we have static methods on types, such as String.IsNullOrEmpty?  The general reason is because the method does not need an instance to perform its work.  But if the method itself considers null to be valid then the method would also need to be static.  So syntactically we cannot allow an instance method to accept null but static methods (and by definition extension methods) we can.

One of the big arguments against this approach is that "extension methods should look and act like instance methods".  Why?  The only real reason I can see is because they look like instance methods when used but we, as developers, are use to the actual code being quite different than what we expect.  The assignment operator, for example, we would never expect to throw an exception yet it can if the type implements the assignment operator and does something inappropriate.  Addition, subtraction and other operators are the same way.    How about the relational operators? 

Data value1 = null;
Data value2 = null;
            
bool result = value1 < value2;

We would never expect the above code to throw an exception and yet a method call is occurring under the hood.  If that method throws an exception then so does the above code.  As developers we have to be aware that we don't know everything that is going on under the hood so we should expect errors anywhere.

Another problem I have with this guideline is the solution.  The guideline is that extension methods should behave like instance methods but that simply isn't possible.  If you call an instance method with a null reference then you will get a NullReferenceException.  This particular exception is one of a handful of system exceptions.  A system exception is an exception that is raised by the runtime proper.  Normal code, aka ours, should not throw system exceptions.  If you explicitly throw a system exception then code analysis tools like FxCop will generate CA warnings about it.  That is what you want.

Instead we have "application" exceptions.  Hence when we receive an argument that is null and we do not allow them then we throw ArgumentNullException.  So to make our extension method behave like an instance method we would need to either throw a system exception explicitly (which we aren't suppose to do) or reference the source variable.  Here's what we'd have to do to our earlier example to get it to throw the appropriate exception while still making code analysis happy.

public static string Leftof ( this string source, string value )
{
   //Force a NullReferenceException
   int len = source.Length;

   if (String.IsNullOrEmpty(value))
      return "";

   ...
}

I have a really big problem writing code just to force something to happen like throw a specific exception.  If I really wanted the exception I'd just throw ArgumentNullException.  But if I do that then I'm no longer making my extension method act like an instance method.  This becomes even more of an issue if you can short-circuit the method return based upon other argument values. 

A final argument for the guideline is that if an extension method eventually becomes an instance method then your application should behave the same but the reality is that you should have unit tests to verify your application's behavior so a major change like going from extension to instance should be thoroughly tested anyway.  Here are my guidelines for extension methods.  Take them with a grain of salt.

  • Extension methods should be placed in a static class called TypeExtension.  For interfaces the "I" can be left off.
  • Extension classes should be only for extension methods and should not contain non-extension code.
  • Each extension method's first parameter should be this Type source.
  • Use an extension method only if the source parameter makes sense and is used in the method.  Use a normal static method otherwise.
  • An extension method should not throw ArgumentNullException for source.    Other arguments are fine.  If source cannot be null then referencing the value will be sufficient to generate the correct exception.
  • If null is not necessarily an invalid value then do not throw any exceptions.  An extension method does not need to follow instance method semantics.
  • Document the behavior if source is null if it is not going to throw an exception.
Posted Sat, Sep 18 2010 by Michael Taylor | no comments
Filed under:
App.config Magic In Visual Studio

One area that causes lots of confusion to developers is the behavior of app.config while running within Visual Studio.  Because VS treats app.config specially it can be confusing when app.config does not work the way we expect.  This post will clarify how the config file is treated while running inside VS.  But first an aside.

Visual Studio Hosting Process

Debugging another process is not trivial.  There are lots of details to worry about.  Consequently debuggers are not simple pieces of code.  Starting a debugger can be expensive.  Beginning a few versions back VS started using the VS hosting process for debugging processes.  This process (vshost) is a wrapper around the debugger.  It allows the debugger to be started onced and then continue to run between debug sessions.  This helps to alleviate the overhead of the debugger and to also allow the debugger to maintain information between debugging sessions. 

The vshost process is started when VS loads a project that is using it.  You can see this by using Task Manager to see the running processes.  The actual process is called <project>.vshost.exe.  It is within this process that your code runs rather than through the normal <process>.exe.  Running through a host process is generally the best idea but it can cause some issues.  Therefore in the Debug properties of the executable's project settings is an option to turn off the hosting process.

App.config

In .NET an application configuration file is required to be called <process>.exe.config.  This is the file that the configuration subsystem will load.  The file must reside in the same directory as the executable.  Because VS recreates the output directory each time you build you cannot just create this file and store it in the output directory.  Instead when you add an Application Configuration File item to your project it is added to the root of the project and called app.config.  This file is automatically copied to the output directory and renamed on each build.  Therefore after each build you will have a brand new configuration file.  Note that if you call the file anything else then this process will not work.  Rarely should you change the name.

For most development you will edit the configuration file in VS.  These changes will be automatically seen when the program runs under the debugger.  However if you make changes to the configuration file at runtime (while running under the debugger) the changes will only persist until the next build. At that time VS will overwrite the file with the root copy.  Therefore you CAN NOT run your program and modify the project's configuration file.  The only way to emulate this behavior would be to copy the (modified) configuration back to the root project and rename it.  As an aside note that if you modify the configuration file at runtime but do not rebuild your code then the configuration changes may persist. 

Which Config Is Which?

One area of confusion is which configuration file your application is actually using while running under the debugger.  Many people will say it is the <process>.exe.config file but that is only partially correct.  Remember the vshost process of earlier?  It complicates things.  Remember that if you are using the VS host (the default) then your process is actually <process>.vshost.exe.  The configuration subsystem knows nothing about the VS host process so it expects to load the <process>.vshost.exe.config file.  When a project is configured to use the hosting process then VS will copy the standard <process>.exe.config file to <process>.vshost.exe.config when the debugger starts.  This gives the appearance that the hosting process is using the application's configuration file when in fact it is VS that is just copying the file for us.  More importantly the configuration subsystem is not impacted by this change at all.  If the program modifies the configuration file while it runs then those changes are made to the <process>.vshost.exe.config file.  The actual process's configuration and the project's app.config files are not modified.   

DLL Config Files - Myth or Fact?

Many class libraries (DLLs) require configuration information.  Most devs like to rely on the configuration subsystem to store this information.  Makes sense.  If you add an application configuration to the class library project then VS will copy the app.config to the output directory and call it <project>.dll.config as you would expect.  It behaves just like a standard Windows application project.  Here's the issue though - the configuration subsystem neither understands nor supports these files.  Let me say it again.  The configuration subsystem WILL NOT load .dll.config files. 

Technically you can change the mappings used by the subsystem to get it to load the files but that is beyond the scope of this article and beyond most applications.  The configuration subsystem will only load the <process>.exe.config file.  There are forum postings all the time from devs who are confused as to why their dll.config file contains some configuration section but the configuration subsystem won't read it.  They believe, incorrectly, that the subsystem uses the configuration file associated with the assembly that is calling it.  That is false.  The subsystem only reads the application's configuration file.

There are two possible workarounds to this issue.  The first workaround, the most common, is to merge the dll.config file into the application's configuration.  This is generally the easiest approach and can be done at build time.  The alternative approach is to read the configuration file manually at runtime.  Some complex libraries follow this route but I cannot recommend it.  That is just too much code to write.