Is there a standard “never returns” attribute for C# functions?

前端 未结 10 1550
青春惊慌失措
青春惊慌失措 2020-12-03 20:42

I have one method that looks like this:

void throwException(string msg)
{
    throw new MyException(msg);
}

Now if I write

         


        
相关标签:
10条回答
  • 2020-12-03 21:20

    You can indicate "never returns" by using generics to declare that the function returns "anything":

    T ThrowException<T>(string msg)
    {
        throw new MyException(msg);
    }
    

    So now you can write:

    int foo(int x, int y)
    {
        if (y == 0)
            return ThrowException<int>("Doh!");
        else
            return x/y;
    }
    

    This idiom is used in languages like Haskell and F#, and is based on the principle of explosion, also known as "ex falso quodlibet". The reasoning is this: if a function never returns, then we can make whatever magical assumptions we want about its return value, since such value will never exist. Here, the caller (foo) assumes ThrowException will return an int.

    A few minor drawbacks:

    • The implementation of ThrowException can circumvent this by returning default(T).
    • You have to specify the return type when calling ThrowException (Haskell and F# can infer it).
    • This idiom is very uncommon in C#, so many people won't recognize it. You may have to add a comment saying what you're doing.

    As the other answers say, you're probably better off returning the exception rather than throwing it.

    0 讨论(0)
  • 2020-12-03 21:21

    Your function

    void throwException(string msg)
    {
        throw new MyException(msg);
    }
    

    add zero value to the code, hence your question is moot. If, on the other hand you want to throw an error with the same message throughout the class and minimise code duplication this is what you should do.

    The normal practice would be to extend MyException for this particular case and throw that:

    public class HomerSimpsonException : MyException
    {
       public HomerSimpsonException() : base ("DOH!!!"){
       }
    }
    int foo(int x, y)
    {
        if (y == 0)
            throw new HomerSimpsonException();
        else
            return x/y;
    }
    

    Even then, that's not complete enough as per Microsoft rule for extending exceptions, there are minimum 4 constructors that you should implement - http://msdn.microsoft.com/en-us/library/ms182151%28VS.80%29.aspx, namely:

      public NewException(){}
      public NewException(string){}
      public NewException(string, Exception){}
      protected or private NewException(SerializationInfo, StreamingContext){}
    
    0 讨论(0)
  • 2020-12-03 21:23

    Bernhof's answer is correct. However, if you are trying to encapsulate a large chunk of logic when instantiating your exception, then all you need to do is change your code from this:

    void throwException(string msg) {
        throw new MyException(msg);
    }
    

    to this:

    Exception makeException(string msg) {
        return new MyException(msg);
    }
    

    Then your calling code will look like this:

    int foo(int x, y) {
        if (y == 0) {
            throw makeException("Doh!");
        }
        return x / y;
    }
    

    All other things being equal, prefer functional code to procedural code. It's easier to re-use and unit-test.

    EDIT:

    In light of Fred's sample code, this is what I would do. It's not a code contract, but it's still functional.

    private int getVarID(string s_varID) {
        int varID;
        if(s_varID == "ILT") {
            return 123;
        } else if(s_varID == "TL") {
            return 456;
        } else if(s_varID == "FT") {
            return 789;
        } else if(int.TryParse(s_varID, out varID)) {
            return varID;
        } else {
            throw makeParseError("varID must be an integer or 'ILT', 'TL' or 'FT'.");
        }
    }
    
    0 讨论(0)
  • 2020-12-03 21:23

    Remove the 'else' keyword, it's redundant anyway, and it will work ;)

    0 讨论(0)
提交回复
热议问题