Is there a way to squeeze "throw new Exception()" into an object?

111 views Asked by At

Some other advanced languages like Haskell and Perl 6 provide syntactic sugar that lets exceptions be thrown, even in a place where the syntax demands an object. It is acts as though it becomes a thrown exception when the value is used (which would be immediately, in the following very contrived example):

enum BuildMode { Debug, MemoryProfiling, Release };

bool IsDebugMode(BuildMode mode)
{
    return mode == BuildMode.Debug ? true
        : mode == BuildMode.MemoryProfiling ? true
        : mode == BuildMode.Release ? false
        : ThrowException<bool>("Unhandled mode: " + mode);
}

The above helper lets an exception be thrown from a place where a value is allowed but not a statement. I can write this function as follows, though it's not as cool as the Haskell or Perl 6 code, since there's no lazy evaluation:

T ThrowException<T>(string message)
{
#line hidden
    throw new Exception(message);
#line default
}

Is there any canonical way to do this, or any good reason not to?

Edit:

I actually didn't try using throw new Exception() as a value in C# 7 until after posting this. That is the answer, more or less. I'll leave this up, in case future people search for what is the C# equivalent to Perl 6's Failure class or Haskell's error.

3

There are 3 answers

0
Heinzi On BEST ANSWER

C# 7.0 supports throw expressions:

return mode == BuildMode.Debug ? true
    : mode == BuildMode.MemoryProfiling ? true
    : mode == BuildMode.Release ? false
    : throw new Exception("Unhandled mode: " + mode);

There is no lazy evaluation, but you don't need your helper method anymore.

0
Panagiotis Kanavos On

I suspect you are looking for throw expressions which were added in C# 7.

return mode == BuildMode.Debug ? true
    : mode == BuildMode.MemoryProfiling ? true
    : mode == BuildMode.Release ? false
    : throw new Exception(...);

One of the most common usages is for null argument validation

var nameValue = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "New name must not be null");

Lazy evaluation

For lazy evaluation you'd have to either return a function or a Lazy :

Lazy<bool> IsDebugMode(BuildMode mode)
{
    bool isDebug() 
    {
        return mode == BuildMode.Debug ? true
            : mode == BuildMode.MemoryProfiling ? true
            : mode == BuildMode.Release ? false
            : throw new Exception(...);
   }

    return new Lazy<bool>(isDebug);
}

And use it as :

var isDbg=IsDebugMode(someMode);
.....
.....
//Will throw here!
if (isDbg.Value)
{
    ...
}

F# provides lazy computations which also return a Lazy with a more convenient syntax :

let isDebugMode mode = 
    match mode with
    | BuildMode.Debug -> true
    | BuildMode.Release -> false
    | _ -> failwith "Ouch!"

let isDbg = lazy (isDebugMode someMode)
...
//Can throw here
if (isDbg.Force() then 
   ...

The same lazy evaluation, using a Func :

Func<bool> IsDebugMode(BuildMode mode)
{
    bool isDebug() 
    {
        return mode == BuildMode.Debug ? true
            : mode == BuildMode.MemoryProfiling ? true
            : mode == BuildMode.Release ? false
            : throw new Exception(...);
   }

    return isDebug;
}

Used as a function :

var isDbg=IsDebugMode(someMode);
...
//Can throw here
if(isDbg())
{
    ...
}

Switch expression

C# 8 will add switch expressions which will probably look like this :

return mode switch {
    BuildMode.Debug           => true,
    BuildMode.MemoryProfiling => true,
    BuildMode.Release         => false,
    _ => throw new Exception (...)
};

The lazy function could look like this :

Lazy<bool> IsDebugMode(BuildMode mode)
{
    bool isDebug() =>
        mode switch {
            BuildMode.Debug           => true,
            BuildMode.MemoryProfiling => true,
            BuildMode.Release         => false,
            _ => throw new Exception (...)
    };

   return new Lazy<bool>(isDebug);
}

Looks a bit more like F#

0
piojo On

The given answers are correct, but I'll add an answer (to my own question) to point out an ideal way to emulate throw expressions in C# 6 and below. It's useful for forward compatibility to have the same name and a similar API, so this is the helper class I settled on:

public class ThrowExpression<T>
{
    public ThrowExpression(string message)
    {
#line hidden
        throw new Exception(message);
#line default
    }

    // never used, but makes the compiler happy:
    public static implicit operator T(ThrowExpression<T> obj)
    {
        return default(T);
    }
}

It's also possible to make a lazy throw expression, which will only throw when it is cast to the target value type. Use your judgment as far as whether to use this class in a way that makes the code less type safe (casting from object to the target type).

public class ThrowExpression<T>
{
    private string message;
    public ThrowExpression(string message)
    {
        this.message = message;
    }

    public static implicit operator T(ThrowExpression<T> obj)
    {
#line hidden
        throw new Exception(message);
#line default
    }
}

Various embellishments are possible, such as accepting different exception types as a parameter or via an additional template argument, but I intend to keep it simple until these enhancements are needed.