@forced

Как грамотно переопределить метод на Generic?

Пишу fluent интерфейс, но возникла проблема

public interface IValidatorExecutorBuilder<T>
{
    IValidatorExecutorBuilder<T> WithAsyncCallback(Func<T, CancellationToken, Task> callback);

    IValidatorExecutorBuilder<T, TResult>
        WithAsyncCallback<TResult>(Func<T, CancellationToken, Task<TResult>> callback);

    ActionResult Execute();
}

public interface IValidatorExecutorBuilder<T, TResult> : IValidatorExecutorBuilder<T>
{
    // вот тут компилятор ругается на то, что должно быть слово new
    ActionResult<TResult> Execute();
}


в данном примере T это модель, которая должна быть в обоих типах интерфейсов
т.е тут получается наследование дженерика, которое расширяется на еще один тип
ну похоже на Task и Task<TResult>.
так вот я что-то думаю что хожу вокруг да около, а вопрос примерно такой

как сделать так, что если я после записи вот такой цепочки

.WithAsyncCallBack((m, t) => Task.FromResult(1)))
.Execute()

на выходе получал не ActionResult, а ActionResult<int>? Ну т.е чтобы метод брался с IValidatorExecutorBuilder<T, TResult>
и соответственно наоборот, если был передан в колбеке просто Task

вопрос глупый, но что-то вообще не вдупляю. все смешалось

т.е тут по большей части переопределение не типа, а возвращаемой сигнатуры, т.е либо non-generic class, либо generic, унаследованного от non-generic

p.s

вот так выглядит вполне жизнеспособно
public interface IValidatorExecutorBuilder<T>
{
    IValidatorExecutorBuilder<T> WithAsyncCallback(Func<T, CancellationToken, Task> callback);
    IValidatorExecutorBuilder<T, TResult> WithAsyncCallback<TResult>(Func<T, CancellationToken, Task<TResult>> callback);
    
    ActionResult Execute();
}

public interface IValidatorExecutorBuilder<T, TResult>
{
    ActionResult<TResult> Execute();
}

но это вариант без наследования интерфейса, плохо или хорошо - хз.

ps2
изменил на вот такую схему
public interface IValidatorExecutorBuilder<T>
{
    IValidatorExecutor<T> WithAsyncCallback(Func<T, CancellationToken, Task> callback);

    IValidatorExecutor<T, TResult>
        WithAsyncCallback<TResult>(Func<T, CancellationToken, Task<TResult>> callback);
}

public interface IValidatorExecutor<T>
{
    IValidatorExecutor<T> ContinueWith(Func<T, CancellationToken, Task> callback);
    ActionResult Execute();
}

public interface IValidatorExecutor<T, TResult>
{
    ActionResult<TResult> Execute();
}


тогда в таком случае будет доступно всего 2 способа использования

return await _validatorResolverService.ForModel(model)
            .WithModelState(ModelState)
            .WithAsyncCallback(async (m, t) => await _accountService.RevokeSession(m, t))
            .ContinueWith()
            .ContinueWith();
            //etc
            return await _validatorResolverService.ForModel(model)
                .WithModelState(ModelState)
                .WithAsyncCallback(async (m, t) => await Task.FromResult(1))
                .Execute();
            // only 1 callback


какой из этих способов предложенных ближе к истине?
  • Вопрос задан
  • 148 просмотров
Решения вопроса 1
AshBlade
@AshBlade Куратор тега C#
Просто хочу быть счастливым
Сделай как Task: вместо интерфейсов - конкретные классы.

Это должны быть различные классы - один Generic (с возвращаемым значением), другой без.
Для совместимости можешь добавить оператор приведения один к другому.

В C# нет перегрузки по возвращаемому значению. В случае наследования интерфейсов (первый пример) ты создаешь метод и идентичной сигнатурой, но при этом различными возвращаемыми значениями. Вот он и ругается.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы