tags:

views:

128

answers:

3

The title is a bit abstract so maybe it is easier to explain with a specific example:

I find it useful to have my exception classes take an enum parameter instead of a string message.

throw new SpecificException(SpecificExceptionCode.ThisThingWentWrong);

There are few reasons for this, including:

  • I can encapulate all the logic for accessing localized string resources in one place
  • I can easily make sure I have string resources for all my exception messages
  • Checking for correct exception messages in unit tests is simpler

I would like to write a base class for this type of exception. Derived exception classes generally just want to provide their own System.Resources.ResourceManager but may also provide additional constructors. The trouble comes because I can only call static methods in calls to base class constructors. This leads me to something like:

public abstract class BaseException : ApplicationException
{
 protected static ResourceManager m_resources;
 public BaseException(System.Enum errorCode, params object[] messageArgs) 
  : base(ProcessError(errorCode, messageArgs))
 {}

 private static string ProcessError(Enum errorCode, params object[] messageArgs)
 {
  string errorMessage = m_resources.GetString(errorCode.ToString());
  // Handling of messageArgs and trace logging
  // ....
  return finalError;
 }
}

and

public class SpecificException : BaseException
{
 static SpecificException()
 {
  m_resources = //.. Get an appropriate resource manager instance
 }

 public SpecificException(SpecificExceptionCode errorCode, params object[] messageArgs)
  : base(errorCode, messageArgs)
 {}
}

This works, but I am unhappy with it as there is no compile time hint that the derived class must provide its own System.ResourceManager. I would like to have a base class such as:

public abstract class BaseException : ApplicationException
{
 protected abstract static ResourceManager Resources{get;}

 public BaseException(System.Enum errorCode, params object[] messageArgs) 
  : base(ProcessError(errorCode, messageArgs))
 {}

 private static string ProcessError(Enum errorCode, params object[] messageArgs)
 {
  string errorMessage = Resources.GetString(errorCode.ToString());
  // Handling of messageArgs and trace logging
  // ....
  return finalError;
 }
}

...but I cannot have an abstract static method. Is there a better way?

A: 

Why does it have to be a static property? You can make the Resources normal property:

protected abstract static ResourceManager Resources{get;}

and just leave it for the implementer to implement it to return a static object:

private static ResourceManager resources = ....

protected override static ResourceManager Resources{get {return resources; }}
Grzenio
protected override static <---!!!!!! ALERT!!!!
leppie
It would be nice to have override and static members! :)))
macropas
+2  A: 

Your current code is broken... there is only one static field; the last static ctor to execute wins.

Re using a non-static method - note that calling virtual/abstract methods in a constructor is a bit dangerous - the concrete class won't have initialized yet, so the override could attempt to use data that isn't yet available.

Can ResourceManager not be an argument to the base-constructor? If they pass null, you use a sensible default... ProcessError would then accept a ResourceManager, etc.

public abstract class BaseException : ApplicationException
{
    static ResourceManager defaultManager;
    static ResourceManager DefaultManager
    {
        get
        {
            if (defaultManager == null) defaultManager = TODO; // sensible default
            return defaultManager;
        }
    }

    public BaseException(System.Enum errorCode, params object[] messageArgs)
        : this(DefaultManager, errorCode, messageArgs) {}
    public BaseException(ResourceManager resourceManager, System.Enum errorCode, params object[] messageArgs)
        : base(ProcessError(resourceManager, errorCode, messageArgs))
    { }

    private static string ProcessError(ResourceManager resourceManager, Enum errorCode, params object[] messageArgs)
    {
        if (resourceManager == null) throw new ArgumentNullException("resourceManager");
        string errorMessage = resourceManager.GetString(errorCode.ToString());
        // Handling of messageArgs and trace logging
        // ....
        return finalString;
    }
}

and:

public class SpecificException : BaseException
{
    static ResourceManager customManager;
    static SpecificException()
    {
        customManager = //TODO - init manager
    }
    public SpecificException(SomeEnum errorCode, params object[] messageArgs)
        : base(customManager, errorCode, messageArgs)
    { }
}

Classes that don't want to provide their own manager just use the other ctor:

: base(errorCode, messageArgs)
Marc Gravell
A: 

Why not have a protected constructor that takes a resource manager as a parameter? That way your concrete exception would have to pass it in.

I would tend to use a factory method rather than the constructor for this kind of issue though.

public static SpecificException SpecificExceptionCodeName()
{
    //Do resource lookup and create Exception
}

and then use

throw ExceptionBuilder.SpecificExceptionCodeName();

this way you get the benefits you outlined without having to inherit from a specific base exception which allows you to throw any Exception you like. There are several places in the framework where it is internally using this method. Note that I tend not to put the factory method on the concrete exception because they showup on derived classes which can get.

jageall