条件通用异常抛出的扩展
Extension for conditional generic exception throwing
我想编写一个扩展方法,当满足某些条件时,它会 生成 和 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,但没有成功。
是的,就像这样使用:
第一个选项(没有针对异常类型的编译器时间验证)
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;
}
第二个选项(对异常类型进行编译时间验证)
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);
}
第一个选项的输出
Name: Alberto and Age: 25
No parameter
exceptionType is not an Exception
工作样本: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);
}
第二个选项的输出
Name: Alberto and Age: 25
No parameter
您可以使用 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;
}
}
很明显,同时指定源类型和异常类型是更好的设计,允许应用通用类型约束。然后为了消除明确指定 TSource
和 TException
的必要性,您可以将 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>();
但是,此 不允许 从 Throw
传回 source
。正如 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 分解为 conditonally-executed 委托,如下所示:
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 构造,但在 no-throw 的情况下 source
可能会返回 ,并且没有必要像前一种情况那样进行额外包装。
第二种方法的一大优点是异常实例化和异常参数的评估都是lazy-evaluated,即只有在异常确实应该是的情况下抛出。这 可能 是一个重要的积极内存和性能因素,消除了 non-exception 代码路径中的 side-effects。
我想编写一个扩展方法,当满足某些条件时,它会 生成 和 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,但没有成功。
是的,就像这样使用:
第一个选项(没有针对异常类型的编译器时间验证)
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;
}
第二个选项(对异常类型进行编译时间验证)
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);
}
第一个选项的输出
Name: Alberto and Age: 25
No parameter
exceptionType is not an Exception
工作样本: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);
}
第二个选项的输出
Name: Alberto and Age: 25
No parameter
您可以使用 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;
}
}
很明显,同时指定源类型和异常类型是更好的设计,允许应用通用类型约束。然后为了消除明确指定 TSource
和 TException
的必要性,您可以将 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>();
但是,此 不允许 从 Throw
传回 source
。正如 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 分解为 conditonally-executed 委托,如下所示:
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 构造,但在 no-throw 的情况下 source
可能会返回 ,并且没有必要像前一种情况那样进行额外包装。
第二种方法的一大优点是异常实例化和异常参数的评估都是lazy-evaluated,即只有在异常确实应该是的情况下抛出。这 可能 是一个重要的积极内存和性能因素,消除了 non-exception 代码路径中的 side-effects。