2

So, I've got a situation where I need to throw an exception because "an argument is not supported". To explain how I got here, this is the rough situation:

  • Ooks come in many types, including Yooks and Zooks
  • Ooks can befriend other Ooks, but only of the right types
  • Yooks can befriend Zooks, but Zooks cannot befriend Yooks

Code example:

public abstract class Ook
{
    public abstract bool TryBefriendYook(Yook yook);
    public abstract bool TryBefriendZook(Zook zook);

    public bool TryBefriend(Ook o0k)
    {
        Type ookType = ook.GetType;

        if (ookType == typeof(Yook))
        {
            TryBefriendYook((Yook)ook);
            return true;
        }
        else if (ookType == typeof(Zook))
        {
            TryBefriendZook((Zook)ook);
            return true;
        }
        else return false;
    }

    public void Befriend(Ook ook)
    {
        if(!TryBefriend(ook))
            throw new Exception(
                "argument type not supported");
    }
}

public sealed class Yook : Ook
{
    public override bool TryBefriendYook(Yook yook)
    {
        return true;
    }
    public override bool TryBefriendZook(Zook zook)
    {
        return true;
    }
}

public partial sealed class Zook : Ook
{
    public override bool TryBefriendYook(Yook yook)
    {
        return false;
    }
    public override bool TryBefriendZook(Zook zook)
    {
        return true;
    }
}

So, this kind of falls under both ArgumentException (the argument isn't right for the subclass) and NotSupportedException (the subclass doesn't accept the argument), doesn't it?

So, which one should I choose - or should I write a custom exception for this situation instead?

Travis Reed
  • 1,572
  • 2
  • 8
  • 11
  • 3
    `NotSupportedException` is thrown when invoked method isn't supported, [msdn](https://learn.microsoft.com/en-us/dotnet/api/system.notsupportedexception?view=netframework-4.7.2) describes the cases. `ArgumentException` or your own is more suitable here – Pavel Anikhouski Apr 03 '19 at 19:33
  • Is there any reason you want to use a specific type of exception? – The Don Apr 03 '19 at 19:40
  • 2
    @PavelAnikhouski In this case, `Zook.BefriendYook` is not supported. This is almost the exact same use case as the first example that msdn listed. @TheDon you're always supposed to throw the most specific exception possible, and *never* throw plain `System.Exception`. – Travis Reed Apr 03 '19 at 20:42

2 Answers2

1

According to MSDN:

NotSupportedException The exception that is thrown when an invoked method is not supported, or when there is an attempt to read, seek, or write to a stream that does not support the invoked functionality.

ArgumentException Class The exception that is thrown when one of the arguments provided to a method is not valid

So according to MSDN, ArgumentException is more suitable to your case edit: if there is absolutely need for more arguments or custom return you can write your own custom exception however there is no problem in using the ArgumentException if it fits your needs.

José Algarra
  • 651
  • 1
  • 6
  • 10
  • The issue is that this is *also* a case of **when an invoked method is not supported** (i.e. `Zook.BefriendYook`) – Travis Reed Apr 03 '19 at 20:30
  • @TravisReed: The caller of `Befriend` didn't invoke an unsupported method. Your implementation of `Befriend` did that. Don't throw exceptions to make the caller deal with bugs in your implementation. (But in fact, your call to `Zook.TryBefriendYook` was supported, and correctly told you the attempt failed) – Ben Voigt Apr 03 '19 at 20:47
1

You should throw neither exception. You've declared this method:

public bool TryBefriend(Ook o0k)

It says that this method expects an argument of type Ook. It someone passes an argument that is of that type or one of its subclasses, and it compiles, then it wouldn't be nice to give them a runtime error and say, in effect, "No, not that type. I really meant this type and this type, but not that one."

If it's possible to try to befriend something but the result is that it may or may not succeed, then trying shouldn't throw an exception. We shouldn't use exceptions as a way to manage normal, predictable program flow.

Think of it like trying to withdraw money from the bank. If you try to withdraw $1000 you should get an message saying that it succeeded or one that it failed because you don't have $1000. It shouldn't return success if you have enough money or throw an exception if you don't. Not having enough money is a normal, predictable possibility. If, on the other hand, the app can't continue because it lost its database connection and there's no way to recover, then throwing an exception would make sense.

So I'd just return true or false, and then the caller can decide what to do based on the response. You'll see this, which makes sense:

if(someOok.TryBefriend(someOtherOook))
{
    // friends!
}
else
{
    // not friends!
}

...and not this, which is awkward:

try
{
    someOok.TryBefriend(someOtherOook);
    // friends!
}
catch(NotSupportedException ex)
{
    // not friends!
}
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62