【问题标题】:Extension Method Returning <T> Instead of Result<T>扩展方法返回 <T> 而不是 Result<T>
【发布时间】:2020-01-17 06:43:09
【问题描述】:

我正在研究 Vladimir Khorikov 的 Result 类以及如何使用它来链接 Result 操作。

他们的原创文章可以在here找到。

他们原来的Result类代码可以在here找到。

我编辑的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 扩展方法来匹配AddAttribToFruitMarkFruitAsBad 的函数签名:

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 实际上是Fruit 而不是我预期的Result&lt;Fruit&gt;

我的扩展方法签名是否有问题,或者我需要根据我的使用方式进行更改?

【问题讨论】:

  • OnFailure&lt;T&gt;(this Result&lt;T&gt; result, Action&lt;T&gt; action)中仔细查看Action&lt;T&gt;
  • 从第一眼看...把Action&lt;T&gt; action改成Action&lt;Result&lt;T&gt;&gt; actionOnFailure
  • @smoksnes 嘿,它确实有效!您可以将此添加为答案,以便我接受吗?另外,您能否解释一下更改OnFailure 的签名如何影响我从OnSuccess 获得的信息?
  • @Floating 你不会从OnSuccess 得到lastFruitResult
  • @John 哦?那么它是从哪里来的呢?

标签: c# generics extension-methods func


【解决方案1】:

您在OnFailure 中的签名略有错误。将其更改为OnFailure&lt;T&gt;(this Result&lt;T&gt; result, Action&lt;Result&lt;T&gt;&gt; action)

基本上,Action 是一个接受多个参数的委托。与Func 的区别在于Action 不返回值。

OnFailure&lt;T&gt;(this Result&lt;T&gt; result, Action&lt;T&gt; action) 会让消费者传递一个带有T 类型的动作作为输入。在您的情况下,TFruit,因为它实际上是在 AddAttribToFruit 中定义的,因为它返回 Result&lt;Fruit&gt;

通过将签名更改为: OnFailure&lt;T&gt;(this Result&lt;T&gt; result, Action&lt;Result&lt;T&gt;&gt; action) 它将让消费者创建一个类型为Result&lt;T&gt; 的操作,在您的情况下为Result&lt;Fruit&gt;

你的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 将是 Actionhere> 中定义的类型。

【讨论】:

  • 只是想在这里加上最后的结论。 FuncAction 确定它在使用 someIdentifer =&gt; 时从被扩展对象中获取的值(和数据类型)。
猜你喜欢
  • 2011-11-06
  • 1970-01-01
  • 2010-12-19
  • 1970-01-01
  • 2023-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多