C# - неявно преобразовать или вывести обобщенный c тип возвращаемого значения - PullRequest
1 голос
/ 04 августа 2020

Я начал использовать шаблон обобщенного типа результата c в качестве объекта-оболочки, который включает возвращаемое значение и информацию об операции, например, успешно ли она завершилась. Вот пример:

public class Researcher
{
    public Result<string> LearnThing(bool factDesired)
    {
        // Validate and bail if failure
        var validationResult = ValidateIntentions(factDesired);
        if (!validationResult.Succeeded)
        {
            // Ideally: return validationResult directly without converting to new instance
            return Result.Failure<string>(validationResult.Error);
        }

        return StateOpinion();
    }

    public Result<string> StateOpinion()
    {
        return Result.Success("I like turtles");
    }

    public Result<bool> ValidateIntentions(bool factDesired)
    {
        if (factDesired)
        {
            // Ideally: no <bool> required, infer default instead
            return Result.Failure<bool>("Only opinions here, sorry");
        }
        else
        {
            return Result.Success(true);
        }
    }
}

public class Result<T>
{
    public bool Succeeded { get; set; }

    public string Error { get; set; }

    public T Value { get; set; }
}

// Static helpers
public static class Result
{
    public static Result<T> Success<T>(T value)
    {
        return new Result<T> { Succeeded = true, Value = value };
    }

    public static Result<T> Failure<T>(string error)
    {
        return new Result<T> { Succeeded = false, Error = error };
    }
}

Здесь общий c Result<T> класс используется для каждого метода, а вспомогательный класс stati c предоставляет механизм для создания результатов с предполагаемым статусом успеха. Пока что это работает нормально.

Единственное, что меня раздражает при таком подходе, это то, что мне нужно часто повторять <T> там, где в идеале это могло бы быть предположено или когда меня больше не волнует T Value (что было бы default) и всего около Error, как в случае сбоев. Я отчасти понимаю, что C# не следует из типов, возвращаемых методом, но я встречал некоторые упоминания неявных операторов , которые, кажется, позволяют некоторые крутые трюки , которых я не могу вполне понимаю.

Итак, я смиренно задаю вопрос C# волшебникам из вас: есть ли какие-то вариации или c я могу добавить к этому подходу, чтобы добиться большего вывода типов и, по сути, неявного Result<"I don't care"> для результатов отказа?

Ответы [ 3 ]

1 голос
/ 13 августа 2020

Вы можете использовать точно такой же метод, как описано в статье, которую вы связали с .

Шаг 1. Вы определяете не-общий вспомогательный класс c для вашего случая сбоя:

public class FailureResult
{
    public string Error { get; }
    
    public FailureResult(string error) { Error = error; }
}

Шаг 2: Вы изменяете свой stati c helper так, чтобы он возвращал FailureResult вместо Result<T>:

public static class Result
{
    ...

    public static FailureResult Failure(string error)
    {
        return new FailureResult(error);
    }
}

Шаг 3: вы определяете неявное преобразование от FailureResult до Result<T>:

public class Result<T>
{
    ...

    public static implicit operator Result<T>(FailureResult result) 
    {
        return new Result<T> { Succeeded = false, Error = result.Error };
    }
}

Шаг 4: Прибыль

public Result<bool> ValidateIntentions(bool factDesired)
{
    if (factDesired)
    {
        // No <bool> required!
        return Result.Failure("Only opinions here, sorry");
    }
    else
    {
        return Result.Success(true);
    }
}

( скрипка )

1 голос
/ 04 августа 2020

Вы можете обойти проблему bool, просто перегрузив:

public static Result<bool> Failure(string error)
{
   return new Result<bool> { Succeeded = false, Error = error };
}

Допустив это:

return Result.Failure("Only opinions here, sorry");

Что касается:

// Ideally: return validationResult directly without converting to new instance
return Result.Failure<string>(validationResult.Error);

Вы могли бы использовать неявный оператор:

public static implicit operator Result<string>(Result<T> result)
{ 
   return Result.Failure<string>(result.Error);
}

Что позволило бы вам сделать:

if (!validationResult.Succeeded)
{
    // Ideally: return validationResult directly without converting to new instance
    return validationResult;
}

Хотя я лично не стал бы этого делать, это неожиданно и неправильное использование языковой функции.

Однако вы можете использовать метод экземпляра или метод расширения:

public Result<string> AsError()
{        
   return Result.Failure<string>(Error);
}

Честно говоря, я думаю, что то, что у вас есть, является декларативным и не пытается быть магом c. Я бы просто придерживался некоторых вспомогательных (расширенных) методов, если это необходимо.

0 голосов
/ 04 августа 2020

Может быть, что-то вроде этого:

    public class Result<T>
    {
        public bool Succeeded { get; set; }
        public string Error { get; set; }
        public T Value { get; set; }
        public bool HasValue { get; protected set; } = true;
    }

    public class Result : Result<object>
    {
        public Result() { HasValue = false; }
    }

    public static class ResultFactory
    {
        public static Result<T> Success<T>(T value)
        {
            return new Result<T> { Succeeded = true, Value = value };
        }

        public static Result Success()
        {
            return new Result { Succeeded = true };
        }

        public static Result Failure(string error)
        {
            return new Result { Succeeded = false, Error = error };
        }
    }

Это позволяет вам не «залезать» где-нибудь в bool, чтобы он соответствовал вашему шаблону. Конечно, вы создаете нулевой объект, но он более или менее спрятан.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...