You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在泛型表达式中向自定义异常构造函数传递参数?

泛型自定义异常抛出的实现问题

直接回答你的问题

不行。当前泛型约束中的new()仅保证TException类型存在无参构造函数,无法直接通过new TException(message, Exception)的方式调用带参构造函数。

现有代码的问题

你的ExceptionThrower<TException>类中,目前只是返回了无参构造的TException实例,没有传入从数据库获取的异常消息和内部异常,完全不符合需求。

可行的替代方案

方案一:利用反射实例化带参构造的异常

通过反射获取TException中接收(string, Exception)参数的构造函数,再创建实例:

public static class ExceptionThrower<TException>
    where TException : Exception, ICustomException
{
    public static async Task<TException> ThrowException(ErrorCode errorCode, Exception exception)
    {
        using var ctx = new ExceptionContext();
        var messageEntity = await ctx.ExceptionMessages
            .Where(x => x.ErrorCode == (int)errorCode)
            .FirstOrDefaultAsync();
        var errorMessage = messageEntity?.Message ?? "未知错误";

        // 获取目标构造函数
        var constructor = typeof(TException).GetConstructor(
            new[] { typeof(string), typeof(Exception) });
        
        if (constructor == null)
        {
            throw new InvalidOperationException(
                $"类型 {typeof(TException).Name} 未定义接收(string, Exception)参数的构造函数");
        }

        // 实例化并返回异常
        return (TException)constructor.Invoke(new object[] { errorMessage, exception });
    }
}

注意:反射会带来一定性能开销,若你的场景是高频异常抛出,需要权衡性能影响。

方案二:通过自定义接口的初始化方法设置参数

修改ICustomException接口,添加初始化方法,让自定义异常实现该方法来设置消息和内部异常:

  1. 定义接口:
public interface ICustomException
{
    void Initialize(string message, Exception innerException);
}
  1. 修改自定义异常实现:
public class DbAddException : Exception, ICustomException
{
    public DbAddException() { }
    public DbAddException(string message) : base(message) { }
    public DbAddException(string message, Exception innerException) : base(message, innerException) { }

    public void Initialize(string message, Exception innerException)
    {
        // 通过反射修改Exception的私有字段,因为Message和InnerException是只读属性
        var messageField = typeof(Exception).GetField("_message", BindingFlags.Instance | BindingFlags.NonPublic);
        var innerExField = typeof(Exception).GetField("_innerException", BindingFlags.Instance | BindingFlags.NonPublic);
        
        messageField?.SetValue(this, message);
        innerExField?.SetValue(this, innerException);
    }
}
  1. 更新ExceptionThrower:
public static class ExceptionThrower<TException>
    where TException : Exception, ICustomException, new()
{
    public static async Task<TException> ThrowException(ErrorCode errorCode, Exception exception)
    {
        using var ctx = new ExceptionContext();
        var messageEntity = await ctx.ExceptionMessages
            .Where(x => x.ErrorCode == (int)errorCode)
            .FirstOrDefaultAsync();
        var errorMessage = messageEntity?.Message ?? "未知错误";

        var exceptionInstance = new TException();
        exceptionInstance.Initialize(errorMessage, exception);
        return exceptionInstance;
    }
}

方案三:通过委托传入异常构造逻辑

移除new()约束,让调用方传入一个构造委托,直接指定异常的创建方式,这是最灵活且无反射开销的方案:

public static class ExceptionThrower<TException>
    where TException : Exception, ICustomException
{
    public static async Task<TException> ThrowException(
        ErrorCode errorCode, 
        Exception exception,
        Func<string, Exception, TException> exceptionFactory)
    {
        using var ctx = new ExceptionContext();
        var messageEntity = await ctx.ExceptionMessages
            .Where(x => x.ErrorCode == (int)errorCode)
            .FirstOrDefaultAsync();
        var errorMessage = messageEntity?.Message ?? "未知错误";

        return exceptionFactory(errorMessage, exception);
    }
}

调用示例:

await ExceptionThrower<DbAddException>.ThrowException(
    ErrorCode.DbAddError, 
    innerException,
    (msg, inner) => new DbAddException(msg, inner));

内容的提问来源于stack exchange,提问作者Stick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 13:00:37