编写一个函数,使用具有常见返回类型的任意对象和方法

本文关键字:返回类型 常见 任意 方法 对象 函数 一个 | 更新日期: 2023-09-27 18:28:09

我有几个不同类型的对象,它们的方法采用不同的参数,都返回相同的类型。这些方法可能会失败。有没有一种方法可以编写一个函数,该函数将接受对象及其方法,指示方法是否失败,并指示发生故障的对象?我不能修改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类,甚至可以访问此函数的返回值。该函数不需要返回,而是可以引发异常。如果出现故障,该功能可能会立即中断。

编写一个函数,使用具有常见返回类型的任意对象和方法

我建议对@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));

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

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

说到这里,我想知道这样做是否值得。事实上,它对执行序列进行了抽象,但我不确定这是否能显著提高可读性(除非您的执行序列比3长,并且预计它在未来会增长)。

最简单的方法是为Status创建一个新的子类,为其填充Failed属性,您可以让构造函数将object作为类型,并在其中调用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);

如果Result也是从Status派生的,那么最不冗长的方法就是修改something()somethingElse(int)someOtherThing(string)来设置字符串本身(但我怀疑这是真的,它看起来Result.Failedbool,但Status.Failedstring)。

一种简单的方法:

//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问题的评论。

您可以为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;
    }
}

则可以使用空合并运算符??:来连锁操作

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

注意到了吗??运算符短路,所以您只在前面的结果都不为空(即失败)的情况下评估somethingElse(3)或someOtherTing("apple"),正如您所希望的,并且最终,如果并且只有当所有调用都成功时,您才返回成功状态。