What are good ways of balancing informative exceptions and clean code?

With our public SDK, we tend to want to give very informative messages about why an exception occurs. For example:

if (interfaceInstance == null)
{
     string errMsg = string.Format(
          "Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
          ParameterInfo.Name,
          ParameterInfo.ParameterType,
          typeof(IParameter)
    );

    throw new InvalidOperationException(errMsg);
}

However, this tends to clutter up the flow of the code, as it tends to put a lot of focus on error messages rather than what the code is doing.

A colleague started refactoring some of the exception throwing to something like this:

if (interfaceInstance == null)
    throw EmptyConstructor();

...

private Exception EmptyConstructor()
{
    string errMsg = string.Format(
          "Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
          ParameterInfo.Name,
          ParameterInfo.ParameterType,
          typeof(IParameter)
    );

    return new InvalidOperationException(errMsg);
}

Which makes the code logic easier to understand, but adds a lot of extra methods to do error handling.

What are other ways to avoid the “long-exception-messages clutter logic” problem? I’m primarily asking about idiomatic C#/.NET, but how other languages manage it are helpful as well.

[Edit]

It would be nice to have the pros and cons of each approach as well.

6

Why not have specialized exception classes?

if (interfaceInstance == null)
{
    throw new ThisParticularEmptyConstructorException(<maybe a couple parameters>);
}

That pushes the formatting and details to the exception itself, and leaves the main class uncluttered.

3

Microsoft seems to (via looking at the .NET source) sometimes use resource/Environment strings. For example, ParseDecimal:

throw new OverflowException(Environment.GetResourceString("Overflow_Decimal"));

Pros:

  • Centralizing exceptions messages, allowing for re-use
  • Keeping the exception message (arguably which don’t matter to code) away from the logic of the methods
  • The type of exception being thrown is clear
  • Messages can be localized

Cons:

  • If one exception message is changed, they all change
  • The exception message is not as easily available to the code throwing the exception.
  • Message is static and contains no information about what values are wrong. If you want to format it, it’s more clutter in the code.

7

For the public SDK scenario, I would strongly consider using Microsoft Code Contracts as these provide informative errors, static checks and you can also generate documentation to add into XML docs and Sandcastle generated help files. It is supported in all paid for versions of Visual Studio.

An additional advantage is that if your customers are using C#, they can leverage your code contract reference assemblies to detect potential problems even before they run their code.

The full documentation for Code Contracts is here.

The technique I use is to combine, and outsource the validation and throwing altogether to a utility function.

The single most important benefit is that it is reduced down to a one-liner in the business logic.

I bet you can’t do better unless you can reduce it further – to eliminate all argument validations and object-state guards from the business logic, keeping only the operational exceptional conditions.

There are, of course, ways to do that – Strongly typed language, “no invalid objects allowed anytime” design, Design by Contract, etc.

Example:

internal static class ValidationUtil
{
    internal static void ThrowIfRectNullOrInvalid(int imageWidth, int imageHeight, Rect rect)
    {
        if (rect == null)
        {
            throw new ArgumentNullException("rect");
        }
        if (rect.Right > imageWidth || rect.Bottom > imageHeight || MoonPhase.Now == MoonPhase.Invisible)
        {
            throw new ArgumentException(
                message: "This is uselessly informative",
                paramName: "rect");
        }
    }
}

public class Thing
{
    public void DoSomething(Rect rect)
    {
        ValidationUtil.ThrowIfRectNullOrInvalid(_imageWidth, _imageHeight, rect);
        // rest of your code
    }
}

[note] I copied this from the question into an answer in case there are comments about it.

Move each exception into a method of the class, taking in any arguments that need the formatting.

private Exception EmptyConstructor()
{
    string errMsg = string.Format(
          "Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
          ParameterInfo.Name,
          ParameterInfo.ParameterType,
          typeof(IParameter)
    );

    return new InvalidOperationException(errMsg);
}

Enclose all of the exception methods into region, and place them at the end of the class.

Pros:

  • Keeps the message out of the core logic of the method
  • Allows for adding logic information to each message (you can pass in arguments to the method)

Cons:

  • Method clutter. Potentially you could have a lot of methods that just return exceptions and aren’t really related to the business logic.
  • Cannot reuse messages in other classes

3

If you can get away with a little more general errors you could write a public static generic cast function for you, that infers the source type:

public static I CastOrThrow<I,T>(T t, string source)
{
    if (t is I)
        return (I)t;

    string errMsg = string.Format(
          "Failed to complete {0}, because type: {1} could not be cast to type {2}.",
          source,
          typeof(T),
          typeof(I)
        );

    throw new InvalidOperationException(errMsg);
}


/// and then:

var interfaceInstance = SdkHelper.CastTo<IParameter>(passedObject, "Action constructor");

There are variations possible (think SdkHelper.RequireNotNull()), that only check requirements on the inputs, and throw if they fail, but in this example combining the cast with producing the result is selfdocumenting and compact.

If you are on .net 4.5, there are ways to have the compiler insert the name of the current method/file as a method parameter (see CallerMemberAttibute). But for an SDK, you probably can’t require your customers to switch to 4.5.

1

What we like to do for business logic errors (not neccessarily argument errors etc.) is to have a single enum that defines all potential types of errors:

/// <summary>
/// This enum is used to identify each business rule uniquely.
/// </summary>
public enum BusinessRuleId {

    /// <summary>
    /// Indicates that a valid body weight value of a patient is missing for dose calculation.
    /// </summary>
    [Display(Name = @"DoseCalculation_PatientBodyWeightMissing")]
    PatientBodyWeightMissingForDoseCalculation = 1,

    /// <summary>
    /// Indicates that a valid body height value of a patient is missing for dose calculation.
    /// </summary>
    [Display(Name = @"DoseCalculation_PatientBodyHeightMissing")]
    PatientBodyHeightMissingForDoseCalculation = 2,

    // ...
}

The [Display(Name = "...")] attributes define the key in the resource files to be used to translate the error messages.

Also, this file can be used as a starting point to find all occurences where a certain type of error is generated in your code.

Checking of Business Rules can be delegated to specialized Validator classes that yield lists of violated Business Rules.

We then use a custom Exception type to transport the violated rules:

[Serializable]
public class BusinessLogicException : Exception {

    /// <summary>
    /// The Business Rule that was violated.
    /// </summary>
    public BusinessRuleId ViolatedBusinessRule { get; set; }

    /// <summary>
    /// Optional: additional parameters to be used to during generation of the error message.
    /// </summary>
    public string[] MessageParameters { get; set; }

    /// <summary>
    /// This exception indicates that a Business Rule has been violated. 
    /// </summary>
    public BusinessLogicException(BusinessRuleId violatedBusinessRule, params string[] messageParameters) {
        ViolatedBusinessRule = violatedBusinessRule;
        MessageParameters = messageParameters;
    }
}

Backend service calls are wrapped in generic error handling code, which translates the violated Busines Rule into an user readable error message:

public object TryExecuteServiceAction(Action a) {
    try {
        return a();
    }
    catch (BusinessLogicException bex) {
        _logger.Error(GenerateErrorMessage(bex));
    }
}

public string GenerateErrorMessage(BusinessLogicException bex) {
    var translatedError = bex.ViolatedBusinessRule.ToTranslatedString();
    if (bex.MessageParameters != null) {
        translatedError = string.Format(translatedError, bex.MessageParameters);
    }
    return translatedError;
}

Here ToTranslatedString() is an extension method for enum that can read resource keys from [Display] attributes and use the ResourceManager to translate these keys. The value for the respective resource key can contain placeholders for string.Format, which match the provided MessageParameters.
Example of an entry in the resx file:

<data name="DoseCalculation_PatientBodyWeightMissing" xml:space="preserve">
    <value>The dose can not be calculated because the body weight observation for patient {0} is missing or not up to date.</value>
    <comment>{0} ... Patient name</comment>
</data>

Example usage:

throw new BusinessLogicException(BusinessRuleId.PatientBodyWeightMissingForDoseCalculation, patient.Name);

With this approach, you can decouple the generation of the error message from the generation of the error, without the need to introduce a new exception class for each new type of error. Useful if different frontends should display different messages, if the displayed message should depend on the user language and/or role etc.

I would use guard clauses as in this example so parameter checks can be reused.

In addition, you can use the builder pattern so more than one validation can be chained. It will look a little bit like fluent assertions.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật