Метод расширения, возвращающий <T>вместо результата <T> - PullRequest
1 голос
/ 17 января 2020

Я изучаю класс Result Владимира Хорикова и как его можно использовать для цепочки результатов.

Их оригинальную статью можно найти здесь .

Их оригинальный Result код класса можно найти здесь .

Мои отредактированные коды класса Result следующие:

public class Result
{
    private bool _isSuccess;
    private string _errorMsg = "";

    public bool IsSuccess()
    {
        return _isSuccess;
    }

    public bool IsFailure()
    {
        return !_isSuccess;
    }

    public string ErrorMsg()
    {
        return _errorMsg;
    }

    public Result(bool isSuccess, string errorMsg)
    {
        bool errorMsgIsEmpty = string.IsNullOrEmpty(errorMsg);

        if (isSuccess && !errorMsgIsEmpty)
        {
            throw new Exception("cannot have error message for successful result");
        }
        else if (!isSuccess && errorMsgIsEmpty)
        {
            throw new Exception("must have error message for unsuccessful result");
        }

        _isSuccess = isSuccess;

        if (!errorMsgIsEmpty)
        {
            _errorMsg = errorMsg;
        }
    }

    public static Result Fail(string errorMsg)
    {
        return new Result(false, errorMsg);
    }

    public static Result<T> Fail<T>(string errorMsg)
    {
        return new Result<T>(default(T), false, errorMsg);
    }

    public static Result OK()
    {
        return new Result(true, "");
    }

    public static Result<T> OK<T>(T value)
    {
        return new Result<T>(value, true, "");
    }

    public static Result Combine(params Result[] results)
    {
        foreach (Result result in results)
        {
            if (result.IsFailure())
            {
                return result;
            }
        }

        return OK();
    }
}

public class Result<T> : Result
{
    private T _value;

    public T Value()
    {
        return _value;
    }

    public Result(T value, bool isSuccess, string errorMsg) : base(isSuccess, errorMsg)
    {
        _value = value;
    }
}

Я работаю со следующими класс тестирования:

public class Fruit
{
    private string _name = "";
    private StringBuilder _attribs;
    public bool isBad;

    public Fruit(string name)
    {
        _name = name;
        _attribs = new StringBuilder();
    }

    public string Name()
    {
        return _name;
    }

    public string Attribs()
    {
        string attribs = _attribs.ToString();

        if (attribs.Length > 0)
        {
            return attribs.Remove(attribs.Length - 2);
        }

        return attribs;
    }

    public void AddAttrib(string attrib)
    {
        _attribs.Append(attrib + ", ");
    }
}

Ниже приведен класс, который работает с Fruit:

public class FruitOperator
{
    public static Result<Fruit> AddAttribToFruit(Fruit fruit, string attrib, bool fail)
    {
        if (fail)
        {
            return Result.Fail<Fruit>("failed");
        }

        fruit.AddAttrib(attrib);

        return Result.OK<Fruit>(fruit);
    }

    public static void MarkFruitAsBad(Fruit fruit)
    {
        fruit.isBad = true;
    }
}

Я создал следующие Result методы расширения для соответствия сигнатурам функций AddAttribToFruit и MarkFruitAsBad:

public static class ResultExtensions
{
    public static Result<T> OnSuccess<T>(this Result<T> result, Func<T, string, bool, Result<T>> func, T val, string str, bool flag)
    {
        if (result.IsFailure())
        {
            return result;
        }

        return func(val, str, flag);
    }

    public static Result<T> OnFailure<T>(this Result<T> result, Action<T> action)
    {
        if (result.IsFailure())
        {
            action(result.Value());
        }

        return result;
    }
}

Моя проблема заключается в том, что я пытаюсь использовать результат OnSuccess в следующей операции:

Fruit fruit = new Fruit("apple");
Result<Fruit> fruitResult = FruitOperator.AddAttribToFruit(fruit, "big", false)
.OnSuccess(FruitOperator.AddAttribToFruit, fruit, "red", true)
.OnFailure(lastFruitResult => FruitOperator.MarkFruitAsBad(lastFruitResult.Value()));

Выше lastFruitResult на самом деле a Fruit вместо ожидаемого Result<Fruit>.

Что-то не так с моими сигнатурами метода расширения или есть что-то, что мне нужно изменить в зависимости от того, как я их использую?

1 Ответ

2 голосов
/ 17 января 2020

Ваша подпись в OnFailure немного неверна. Измените его на OnFailure<T>(this Result<T> result, Action<Result<T>> action).

Обычно Action - это делегат, который принимает ряд параметров. Отличие от Func состоит в том, что Action не возвращает значение.

OnFailure<T>(this Result<T> result, Action<T> action) позволит потребителю передать действие с типом T в качестве ввода. В вашем случае T - это Fruit, так как он фактически определен в AddAttribToFruit, потому что он возвращает Result<Fruit>.

Изменяя подпись на: OnFailure<T>(this Result<T> result, Action<Result<T>> action), это позволит потребителю создать действие с тип Result<T>, который в вашем случае равен Result<Fruit>.

Ваш OnFailure должен выглядеть примерно так:

   public static Result<T> OnFailure<T>(this Result<T> result, Action<Result<T>> action)
   {
        if (result.IsFailure())
        {
            action(result); // Note that result is Result<T> and action takes Result<T> as parameter
        }

        return result;
    }

lastFruitResult будет типом, определенным в Действие <<em> здесь >.

...