1

我有几个不同类型的对象,它们的方法采用不同的参数,它们都返回相同的类型。这些方法可能会失败。有没有办法编写一个函数来获取对象及其方法,指示方法是否失败,并指示发生故障的对象?我无法修改 Result 对象,并且它不包含有关调用它的对象的信息。

我觉得这有点冗长:

Result resultA = A.something();
if(resultA.Failed) return new Status{Failed=A.GetType().ToString()};

Result resultB = B.somethingElse(3);
if(resultB.Failed) return new Status{Failed=B.GetType().ToString()};

Result result3 = C.someOtherThing("apple");
if(resultC.Failed) return new Status{Failed=C.GetType().ToString()};

// Do some processing of the results (will succeed if A,B,C succeeded)

return new Status {Failed=null, Success=true};

有没有办法将所有这些封装到一个函数中?它似乎非常重复。A、B 和 C 不是从有用的基类继承的,它们的方法都采用不同的参数并具有不同的名称。

话虽如此,我确实可以访问 Status 类,甚至可以访问该函数的返回值。该函数不需要返回,它可以抛出异常。如果出现故障,该功能可以立即中断。

4

4 回答 4

3

我建议对@Shawn Holzworth 的回答做一个更简单的改动。

首先,因为你的方法唯一的共同点是它们的返回类型,这是我们在这里唯一可以概括的东西。我们可以构造一个方法来按照您的要求处理执行,如下所示:

 public static Status ExecuteRequests(params Func<Result>[] actions){
  foreach (Func<Result> action in actions) {
    Result r = action();
    if (!r.Success) {
      Status s = new Status() { Failed = action.Target.GetType().ToString() };
      return s;
    }
  }

  return new Status() { Success = true };
}

和呼叫站点:

  ExecuteRequests(
    () => A.doSomething(), 
    () => B.doSomethingElse(42));

如果您想对结果执行额外的计算,您可以扩展 ExecuteRequests 方法以在 Action 形式中输入处理程序。

这种抽象实际上促进了您没有问过但我认为值得一提的另一件事:并行执行。当你像这样封装这些操作时,很容易利用 TPL,并行发送请求并在到达时聚合它们,但这有点复杂。

上面提到了,我想知道这是否值得努力。实际上,它抽象了执行序列,但我不确定这是否会显着提高可读性(除非您的执行序列比 3 更长,并期望它在未来增长)。

于 2013-08-20T06:15:09.547 回答
0

最简单的方法是创建一个新的子类,为您Status填充Failed属性,您可以让构造函数将 aobject作为类型并GetType()在那里调用。

public class FailedStatus : Status
{
    public FailedStatus(object source)
    {
        this.Failed = source.GetType().ToString();
    }
}

Result resultA = A.something();
if(resultA.Failed) return new FailedStatus(A);

Result resultB = B.somethingElse(3);
if(resultB.Failed) return new FailedStatus(B);

Result result3 = C.someOtherThing("apple");
if(resultC.Failed) return new FailedStatus(C);

如果ResultStatus也派生出最不冗长的方法是 modify something(), somethingElse(int), andsomeOtherThing(string)设置字符串本身(但我怀疑这是真正的意义,它看起来Result.Failed是 abool但是Status.Failed是 a string)。

于 2013-08-19T16:19:03.650 回答
0

您可以为 Result 创建一个扩展方法,在成功的情况下返回 null,如下所示:

static class ResultExtension 
{
    public static Status GetStatus<T>(this Result res, T a) {
        return res.Failed? new Status{Failed=a.GetType().ToString()} : null;
    }
}

然后您可以使用null-coalescing 运算符 ??链接操作:

return A.something().GetStatus(A)             ??
       B.somethingElse(3).GetStatus(B)        ??
       C.someOtherThing("apple").GetStatus(C) ??
       new Status{Failed=null, Success=true};

请注意?? operator short-circuits,所以你只评估 somethingElse(3) 或 someOtherThing("apple") 如果之前的结果都不是 null (即失败),你想要并且最后,当且仅当所有调用都成功了你返回成功状态。

于 2013-08-19T17:24:29.363 回答
0

一种简单的方法可以做到这一点:

//Note: the method signature could just be IEnumerable<Result> AggregateResults(params Func<Result>[]) 
//if you want to be able to aggregate the results of 0 calls
IEnumerable<Result> AggregateResults(Func<Result> func, params Func<Result>[] otherFuncs)
{
    yield return func();
    foreach(var otherFunc in otherFuncs)
        yield return otherFunc();
}

//Usage:
var results = AggregateResults(
    () => A.Something(),
    () => B.SomethingElse(3),
    () => C.SomethingOtherThing("apple"));

不幸的是,在第一次失败的调用上停止并获取调用对象的要求使它稍微困难了一点:

class AggregateResult
{
    public object CallingObject;
    public Result Result;

    public static AggregateResult Create<T>(T t, Func<T,Result> func) 
    { 
        return new AggregateResult() { CallingObject = t, Result = func(t) }; 
    }
}
IEnumerable<AggregateResult> AggregateResults(Func<AggregateResult> func, params Func<AggregateResult>[] otherFuncs)
{
    yield return func();
    foreach (var otherFunc in otherFuncs)
        yield return otherFunc();
}
//Usage:
var results = AggregateResults(
            () => AggregateResult.Create(A, x=>x.Something()),
            () => AggregateResult.Create(B, x=>x.SomethingElse(3)),
            () => AggregateResult.Create(C, x=>x.SomethingOtherThing("apple")));
var failedResult = results.FirstOrDefault(x => x.Result.Failed);
if (failedResult != null) return new Status() { Failed = failedResult.CallingObject.GetType().ToString() };

也就是说,我同意这听起来像 XY 问题的评论。

于 2013-08-19T17:02:23.783 回答