抛出条件泛型异常的扩展

时间:2015-12-29 10:27:22

标签: c# generics extension-methods

我想编写一个扩展方法,当某些条件满足时,生成 thow 泛型异常。我想出了这段代码:

internal static TSource ConditionalThrow<TSource, TException>(this TSource source, Func<TSource, bool> throwCondition, TException exception, params object[] arguments) where TException : Exception
{
    if (throwCondition(source))
    {
        throw CreateInstance(exception.GetType(), arguments) as Exception;
    }
    else
    {
        return source;
    }
}

问题在于用户必须自己初始化exception参数,因此可以简化为:

internal static TSource ConditionalThrow<TSource, TException>(this TSource source, Func<TSource, bool> throwCondition, TException exception) where TException : Exception
{
    if (throwCondition(source))
    {
        throw exception;
    }
    else
    {
        return source;
    }
}

但我希望该扩展会产生错误。我想是通过Type代替TException,但我不能将其限制为Exception类型。

internal static TSource ConditionalThrow<TSource>(this TSource source, Func<TSource, bool> throwCondition, Type exception)
{
    if (throwCondition(source))
    {
        throw CreateInstance(exception, arguments) as Exception;
    }
    else
    {
        return source;
    }
}

我能否实际实现生成 thow 一般性异常的扩展?

我调查了this,但没有成功。

3 个答案:

答案 0 :(得分:2)

是的,就像使用它一样:

第一个选项(对于exeception类型没有编译器时间验证)

internal static TSource ConditionalThrow<TSource>(this TSource source, Func<TSource, bool> throwCondition, Type exceptionType, params object[] arguments)
{
    if (!typeof(Exception).IsAssignableFrom(exceptionType))
        throw new ArgumentException("exceptionType is not an Exception");
    if (throwCondition(source))
        throw Activator.CreateInstance(exceptionType, arguments) as Exception;
    return source;
}

第二个选项(使用exeception类型的编译器时间验证)

internal static TSource ConditionalThrow<TSource>(this TSource source, Func<TSource, bool> throwCondition, Func<Exception> exeptionBuilder)
{
    if (throwCondition(source))
        throw exeptionBuilder();
    return source;
}

让我们使用示例类来测试我们的解决方案

public class Temp
{
    public string Name { get; set; }
}

public class MyException : Exception
{
    public MyException(string name, string age)
        : base($"Name: {name} and Age: {age}")
    { }

    public MyException()
        : base("No parameter")
    { }
}

测试第一个选项

try
{
    new Temp().ConditionalThrow(t => true, typeof(MyException), "Alberto", "25");
}
catch (MyException ex)
{
    Console.WriteLine(ex.Message);
}
try
{
    new Temp().ConditionalThrow(t => true, typeof(MyException));
}
catch (MyException ex)
{
    Console.WriteLine(ex.Message);
}      
try
{
    new Temp().ConditionalThrow(t => true, typeof(string));
}
catch (ArgumentException ex)
{
    Console.WriteLine(ex.Message);
}

第一个选项的输出

  

姓名:Alberto和年龄:25

     

无参数

     

exceptionType不是异常

工作样本:https://dotnetfiddle.net/brIjq9

测试第二个选项

try
{
    new Temp().ConditionalThrow(t => true, () => new MyException("Alberto", "25"));
}
catch (MyException ex)
{
    Console.WriteLine(ex.Message);
}
try
{
    new Temp().ConditionalThrow(t => true, () => new MyException());
}
catch (MyException ex)
{
    Console.WriteLine(ex.Message);
}

第二个选项的输出

  

姓名:Alberto和年龄:25

     

无参数

工作样本:https://dotnetfiddle.net/8ZQiIc

答案 1 :(得分:1)

很明显,指定源类型和异常类型是更好的设计,允许应用泛型类型约束。然后,为了消除明确指定TSourceTException的必要性,您可以将ConditionalThrow分解为两个方法,如下所示:

internal static void Throw<TException>(this bool condition, params object[] args) where TException : Exception
{
    if (condition) throw (Exception)Activator.CreateInstance(typeof(TException), args);
}

internal static bool If<TSource>(this TSource source, Func<TSource,bool> condition)
{
    return condition(source);
}

然后用法就是这样:

10.If(x => x>0).Throw<Exception>();

但是,此不允许允许sourceThrow返回。正如OP正确建议的那样,这里的解决方法是使用另一种确保返回source的方法来包装调用:

internal static TSource Do<TSource> (this TSource source, Action<TSource> action)
{ 
    action(source); 
    return source; 
}

int a = 10.Do(s => s.If(x => x>0).Throw<Exception>()) + 1;

另一种选择是将异常的 instatiation 分解为一个条件执行的委托,如下所示:

internal static TSource ThrowIf<TSource>(this TSource source, Func<TSource,bool> condition, Func<Exception> buildException)
{
    if (condition(source))
        throw buildException();
    else
        return source;
}

然后用法就是这样:

int a = 10.ThrowIf(x => x>0, () => new Exception()) + 1;

虽然这需要一个() => lambda构造,但是如果没有抛出,source 可以返回,并且不需要像前案例。

第二种方法的一大优点是异常实例化和异常参数的评估都是惰性评估的,即仅在真正应该抛出异常的情况下。这个可能是一个严重的正记忆和性能因素,消除了非异常代码路径中的副作用。

答案 2 :(得分:0)

您可以使用new()通用约束,以便在方法中创建例外。缺点是你来指定通用参数,不能推断它们。

internal static TSource ConditionalThrow<TSource, TException>(this TSource source, Func<TSource, bool> throwCondition)
    where TException : Exception, new()
{
    if (throwCondition(source)) {
        throw new TException();
    } else {
        return source;
    }
}

或者你可以使用自己创建异常的当前方法,但是通过泛型参数而不是Type参数指定类型。

internal static TSource ConditionalThrow<TSource, TException>(this TSource source, Func<TSource, bool> throwCondition)
    where TException : Exception
{
    if (throwCondition(source)) {
        throw CreateInstance(typeof(TException), arguments) as Exception;
    } else {
        return source;
    }
}