代码之家  ›  专栏  ›  技术社区  ›  piojo

有没有办法将“throw new Exception()”压缩到对象中?

  •  2
  • piojo  · 技术社区  · 7 年前

    其他一些高级语言如Haskell和Perl 6提供了语法糖,允许抛出异常,即使在语法要求对象的地方也是如此。当使用该值时,它的行为就好像它变成了抛出的异常(在下面这个非常做作的示例中,它将立即成为抛出的异常):

    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);
    }
    

    上面的助手允许从允许值但不允许语句的位置引发异常。我可以按如下方式编写这个函数,尽管它不像Haskell或Perl 6代码那样酷,因为没有惰性计算:

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

    有没有什么正统的方法可以做到这一点,或者有什么好的理由不这样做?

    编辑:

    我其实没试着用 throw new Exception() 作为C#7中的值,直到发布此内容。或多或少,这就是答案。我不说了,以防将来人们搜索与Perl6相当的C Failure 类或Haskell的 error .

    3 回复  |  直到 5 年前
        1
  •  6
  •   Heinzi    7 年前

    C#7.0支架 throw expressions :

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

    没有懒惰的评估,但是你不再需要你的助手方法了。

        2
  •  3
  •   Panagiotis Kanavos    7 年前

    我想你在找 throw expressions 在C#7中添加的。

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

    最常见的用法之一是空参数验证

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

    懒惰的评价

    对于懒惰的评估,您必须返回 功能 或者懒汉:

    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);
    }
    

    并将其用作:

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

    提供 lazy computations 它还返回一个更方便语法的Lazy:

    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 
       ...
    

    同样的惰性计算,使用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;
    }
    

    用作函数:

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

    开关表达式

    C#8将添加开关表达式 可能 看起来像这样:

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

    lazy函数可能如下所示:

    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);
    }
    

    看起来有点像F#

        3
  •  0
  •   piojo    7 年前

    给出的答案是正确的,但是我将添加一个答案(到我自己的问题中)来指出一种模拟C#6及以下的抛出表达式的理想方法。对于前向兼容性来说,具有相同的名称和相似的API是很有用的,因此这是我解决的helper类:

    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);
        }
    }
    

    还可以生成一个lazy throw表达式,该表达式只有在强制转换为目标值类型时才会抛出。判断是否以使代码不太安全的方式使用这个类(从 object 到目标类型)。

    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
        }
    }
    

    各种修饰都是可能的,例如接受不同的异常类型作为参数或通过附加的模板参数,但是我打算在需要这些增强之前保持简单。

    推荐文章