powershellexceptionextending

Extending Exceptions in Powershell with default message


I have been trying to wrap my head around extending Exceptions in PowerShell. My first pass at a class looks like this...

class CustomException : Exception {
    CustomException () {
    }
    CustomException ([String] $message) : base ($message) {
    }
}

And use is as expected, with the first approach here producing the type name, and the second the provided message...

try {
    throw [CustomException]
} catch {
    "$($_.Exception.Message)"
}
try {
    throw [CustomException] 'Another message'
} catch {
    "$($_.Exception.Message)"
}

However, I really want to have a default message, so I can use the first example in many places, but if I want to revise the message I can do it the one time in the class. Or perhaps even localize the message at some point. This thread seems to suggest it's possible in C#, especially the last two posts. So, taking the last example there...

public class MyException : Exception
{
    public MyException () : base("This is my Custom Exception Message")
    {
    }
}

I thought I could do the same in Powershell, like so...

CustomException () : base ('Default message') {
}

But I still get the type name when not providing a message. That got me thinking, and I tried...

try {
    throw [System.IO.FileNotFoundException]
} catch {
    "$($_.Exception.Message)"
}

And that ALSO doesn't provide a default message, just the class name. So, is that C# code not doing what I think it's doing? Or is this just a difference in behavior in Powershell? Or am I doing something wrong?


Solution

  • What you want is perfectly supported, but you need to throw an instance of the exception!

    The syntax of throw is basically:

    throw [Throwable]
    

    where Throwable is either an ErrorRecord, an Exception, or a string (a bare error message basically).

    When you throw the type literal [CustomException], PowerShell converts that expression to a [string], which is why you just see the type name in the catch block.

    Correctly throwing an exception instance requires you to call the constructor:

    class CustomException : Exception
    {
      CustomException() : base("default CustomException message goes here")
      {
      }
    }
    
    try {
        throw [CustomException]::new()    # <-- don't forget to call the constructor
    } catch {
        "$_"
    }