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

前端 未结 10 1549
青春惊慌失措
青春惊慌失措 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:00

    No. I suggest you change the signature of your first function to return the exception rather than throw it, and leave the throw statement in your second function. That'll keep the compiler happy, and smells less bad as well.

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

    Why not just change it to

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

    This gives the same runtime results, and the compiler won't complain.

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

    Don't hand the exception creation off to another function (i.e. just throw it directly) and the compiler won't complain. Handing off to a "helper" type function for exception throwing is a waste of time unless the function is actually adding value to the exception process.

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

    The [DoesNotReturn] attribute in System.Diagnostics.CodeAnalysis should be what you want.

    Reference: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.doesnotreturnattribute?view=netcore-3.1

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

    Well, this is a "pretty" low-effort implementation.

    Working class:

    /// <summary>
    /// Representation of an unreachable type, exposing a method to represent unreachable code.
    /// </summary>
    public static class Unreachable {
    
        /// <summary>
        /// Representation of unreachable code with return semantics.
        /// </summary>
        public static dynamic Code() {
            throw new NotImplementedException(@"Unreachable code was reached.");
        }
    }
    

    Example:

    public object[] UnreachableCodeTest() {
        return Unreachable.Code();
    }
    

    Decompiled:

    Offset  OpCode  Operand
    0   ldsfld  System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
    5   brtrue.s    -> (10) ldsfld System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
    7   ldc.i4.0    
    8   ldtoken System.Object[]
    13  call    System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)
    18  ldtoken TestApp.Program
    23  call    System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)
    28  call    System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder::Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,System.Type,System.Type)
    33  call    System.Runtime.CompilerServices.CallSite`1<!0> System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>>::Create(System.Runtime.CompilerServices.CallSiteBinder)
    38  stsfld  System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
    43  ldsfld  System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
    48  ldfld   !0 System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>>::Target
    53  ldsfld  System.Runtime.CompilerServices.CallSite`1<System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>> TestApp.Program/<UnreachableCodeTest>o__SiteContainere6::<>p__Sitee7
    58  call    System.Object TestApp.Unreachable::Code()
    63  callvirt    !2 System.Func`3<System.Runtime.CompilerServices.CallSite,System.Object,System.Object[]>::Invoke(!0,!1)
    68  ret 
    

    Optimize by implementing something w/ Fody, searching for this call signature and replacing it with a single raw ret (if it'll let you) or some other simple acceptable low-cost alternative.

    (Edit)

    As you mod me down, I will be forced to explain why this is effective and useful.

    Mostly, this goes into a block that resembles at the end of a method that has a logically unreachable path;

    #pragma warning disable 162
    // ReSharper disable CSharpWarnings::CS0162
    // ReSharper disable HeuristicUnreachableCode
    return Unreachable.Code();
    // ReSharper restore HeuristicUnreachableCode
    // ReSharper restore CSharpWarnings::CS0162
    #pragma warning restore 162
    

    If you modify the code above this section in a way that allows the code to be reached, your tests will fail. If you have code below, you're wrong and the compiler will let you know. Once you move beyond initial maturity, you would remove this code block all together. The main purpose of this is to catch scenarios where the code is not unreachable when it should be.

    In other scenarios where code should not arrive but can't be logically excluded by the compiler (a default statement in a case, for example), you would traditionally throw an error. If you want to optimize for this scenario, you need this sort of implementation.

    select( thisIsAlways123Never0OrGreaterThan3 ) {
        default: return Unreachable.Code();
        case 1: DoSomething(); break;
        case 2: DoSomethingElse(); break;
        case 3: return GetSomething();
    }
    

    To optimize such that minimal instructions are emitted for the default: path with this minimally written code, you need a friend named Fody.

    The ideal scenario is a result similar to what C++ developers desire in the GCC & LLVM __builtin_unreachable() or MSVC __assume(0) or __declspec(noreturn) on an empty method.

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

    Bernhof already gave you a way to avoid the compiler complain. However, also be aware your stack trace will be off (and some logger libraries won't handle util-classed-i-throw-exceptions-for-your-app methods) which makes debugging your application harder.

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