You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Dotnet中抽象基类泛型方法(含泛型返回类型与泛型参数)的重写问题

Dotnet中抽象基类泛型方法(含泛型返回类型与泛型参数)的重写问题

嗨,我明白你的困扰了——你想让每个子类Parser返回特定的Result<HtmlDocument>,但基类的泛型方法设计和子类的实现需求没对上,导致编译器报错。咱们一步步拆解问题,再给出合适的解决方案。

问题出在哪?

你基类里的Process<T, TReturn>是一个开放泛型方法:它允许调用者传入任意符合约束的TTReturn类型。但你的子类IndexParser想把返回值固定成Result<HtmlDocument>,这相当于改变了基类方法的泛型签名——基类要求返回的是任意TReturn,但你硬把它换成了具体类型,编译器自然会认为你不是在重写基类方法,而是在定义新方法,所以就会报“找不到要重写的方法”的错误。

反过来,如果你强行保留基类的泛型签名,又会被要求返回TReturn,而不是你想要的Result<HtmlDocument>,这就陷入了矛盾。

解决方案:调整基类设计(推荐方案)

既然每个子类Parser都是处理特定的输入类型,返回特定的Result<XXX>,那更合理的做法是把基类改成泛型基类,让每个子类明确自己的输入和输出类型,这样既符合类型安全,又不会有重写冲突。

1. 修改泛型基类

BaseParser改成带两个泛型参数的抽象类,分别指定输入类型和结果类型:

public abstract class BaseParser<TInput, TResult> where TInput : class
{
    // 这里直接返回固定的Result<TResult>,不需要额外的泛型参数
    protected abstract Result<TResult> Process(HttpClient httpClient, TInput data);
}

2. 实现子类IndexParser

现在子类可以明确指定自己处理的输入类型(比如假设你要传入的是string类型的URL)和返回的Result<HtmlDocument>

// 替换YourInputType为你实际要处理的输入类,比如string、自定义RequestModel等
public class IndexParser : BaseParser<string, HtmlDocument>
{
    protected override Result<HtmlDocument> Process(HttpClient httpClient, string data)
    {
        // 在这里写你的解析逻辑:调用httpClient、解析HtmlDocument
        try
        {
            var response = httpClient.GetStringAsync(data).GetAwaiter().GetResult();
            var htmlDoc = new HtmlDocument();
            htmlDoc.LoadHtml(response);
            
            return new Result<HtmlDocument>
            {
                Success = true,
                Value = htmlDoc,
                Message = "Index页面解析成功"
            };
        }
        catch (Exception ex)
        {
            return new Result<HtmlDocument>
            {
                Success = false,
                Message = $"解析失败:{ex.Message}"
            };
        }
    }
}

备选方案:保留非泛型基类(不推荐,仅作参考)

如果因为某些原因不能修改基类为泛型,那可以调整基类方法的泛型约束,让返回类型必须是Result<TResult>,然后在子类里做运行时类型检查(但这种方式会失去编译时类型安全,不推荐):

1. 修改基类方法

public abstract class BaseParser
{
    // 把返回类型固定为Result<TResult>,只保留输入类型的泛型参数
    protected abstract Result<TResult> Process<TInput, TResult>(HttpClient httpClient, TInput data) 
        where TInput : class;
}

2. 实现子类

public class IndexParser : BaseParser
{
    protected override Result<TResult> Process<TInput, TResult>(HttpClient httpClient, TInput data)
    {
        // 只处理我们关心的输入类型和返回类型
        if (typeof(TInput) == typeof(string) && typeof(TResult) == typeof(HtmlDocument))
        {
            var url = data as string;
            var response = httpClient.GetStringAsync(url).GetAwaiter().GetResult();
            var htmlDoc = new HtmlDocument();
            htmlDoc.LoadHtml(response);
            
            return new Result<HtmlDocument>
            {
                Success = true,
                Value = htmlDoc,
                Message = "解析成功"
            } as Result<TResult>;
        }

        // 处理不支持的类型
        return new Result<TResult>
        {
            Success = false,
            Message = $"IndexParser不支持处理输入类型{typeof(TInput)}或返回类型{typeof(TResult)}"
        };
    }
}

总结

第一种泛型基类的方案是最符合C#类型设计原则的,它让每个子类的职责更清晰,同时保证了编译时的类型安全,避免了运行时的类型检查。

备注:内容来源于stack exchange,提问作者julianuslemurrex

火山引擎 最新活动