【问题标题】:How do I make a generic wrapper method that returns the wrapped method's result, if it has one?如果有一个返回包装方法结果的通用包装方法,我该如何创建?
【发布时间】:2019-07-10 21:54:04
【问题描述】:

我正在尝试创建一个接受另一个方法的方法,并返回内部方法返回的值,如果该内部方法不是 void。我想这样做而不区分 Func<>Action<>

本质上,我希望包装方法的行为与未包装方法完全相同,并加上包装器提供的一些功能。目标很简单,但实现起来很难。

public int ReturnsInteger() {
    Console.WriteLine("I return 42");
    return 42;
}

public static T WrapperMethod(Func<T> someMethod) {
    Console.WriteLine("Wrap start");
    var result = someMethod();
    Console.WriteLine("Wrap end");
    return result;
}

private static void Main() {
    var X = WrapperMethod(()=>ReturnsInt());
    Console.WriteLine("X = " + X);
    // Wrap start
    // I return 42
    // Wrap end
    // X = 42
}
public void ReturnsNothing() {
    Console.WriteLine("I return nothing");
    return;
}

public static T WrapperMethod(Action someMethod) {
    Console.WriteLine("Wrap start");
    someMethod();
    Console.WriteLine("Wrap end");
}

private static void Main() {
    WrapperMethod(()=>ReturnsNothing());
    // Wrap start
    // I return nothing
    // Wrap end
}

【问题讨论】:

  • 您将需要对ActionFunc 进行重载,但您可以实现Unit 类型,以由Actions 的包装器返回。跨度>
  • 您想出的任何解决方案都将从根本上 更复杂,并且执行起来从根本上 比您这里的解决方案更差。试图概括这一点绝不会简化这一点。只支持两个不同的代表。
  • "我正在尝试创建一个接受另一个方法的方法,并返回内部方法返回的值,如果该内部方法不是 void " -- 我什至不知道那是什么意思。也就是说,如果内部方法的返回类型为void,那么您希望您的方法返回一个值吗? C# 不能那样工作。方法总是要么返回一个值,要么不返回一个值……你不能让它们根据参数有条件地返回一个值。
  • 这里只满足了一半的要求。为了使它们完整,如果内部方法确实返回 void,您必须指定您期望包装器方法执行的操作。
  • 你们都提出了好点。我挣扎了将近一个小时,试图弄清楚如何提出正确的问题,但@PeterDuniho 的回答抓住了问题的要旨。

标签: c# generics methods delegates


【解决方案1】:

如上所述,我不完全理解您问题的措辞。但是,基本场景似乎很简单:您希望一般地修饰未知方法的行为,同时处理返回值和不返回值的方法。

每当我遇到这种类型的场景时,我都会通过编写一个返回类型为void 的包装器方法来解决它,然后通过 lambda 适应第二个包装器方法。例如:

T Wrapper<T>(Func<T> func)
{
    T result = default(T);
    Wrapper(() => { result = func(); });
    return result;
}

void Wrapper(Action action)
{
    Console.WriteLine("Wrap start");
    action();
    Console.WriteLine("Wrap end");
}

这样我只需要编写一次包装逻辑。 Func&lt;T&gt; 版本必须创建一个新的委托实例并捕获一个局部变量有一些开销,但至少当我不得不做这种事情时,包装逻辑和包装逻辑足够复杂,以至于lambda 无关紧要。

【讨论】:

  • 感谢您的回答。 C# 必须将 void 方法与其他方法分开处理,这对我来说似乎很奇怪,但这似乎足够优雅。
  • 无论好坏,C# 都遵循 C 和类似语言设置的约定,所有这些约定都处理具有不同返回类型的方法。我想 C# 设计者可能已经偏离了那个规范,我怀疑它是基于底层架构的(对于基于 x86 的处理器,返回值与否之间存在区别),但这可能会引起程序员的更多恐慌试图从其他语言转移。事实上,我不知道有任何主流的静态类型语言不具备这种区别。
  • @PeterDuniho 有趣的是,有一些实际用途。见stackoverflow.com/questions/5450748/what-is-system-void。我承认我永远不需要它们!
【解决方案2】:

我会尝试通过实际给方法返回一些东西来消除 void 返回方法的基本语言差异。

通过实现Unit 类型,即只是一个值struct,然后您可以将Action 转换为Func&lt;Unit&gt;。这实际上只是使用了适配器模式。

现在您只需要处理一种形式为Func&lt;T&gt; 的委托。然后,您可以将所有艰苦的工作投入到单个 Wrapper 方法中。

试试这个:

void Main()
{
    var result = Wrapper(DoSomething);
}

private void DoSomething()
{
    Console.WriteLine("Test.");
}

T Wrapper<T>(Func<T> func)
{
    Console.WriteLine("Wrap start");
    var result = func();
    Console.WriteLine("Wrap end");
    return result;
}

Unit Wrapper(Action action)
{
    return Wrapper(() => { action(); return Unit.Default; });
}

/// <summary>
/// Represents a type with a single value. This type is often used to denote the successful completion of a void-returning method (C#) or a Sub procedure (Visual Basic).
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct Unit : IEquatable<Unit>
{
    public static Unit Default => default(Unit);
    public bool Equals(Unit other) => true;
    public override bool Equals(object obj) => obj is Unit;
    public override int GetHashCode() => 0;
    public override string ToString() => "()";
    public static bool operator ==(Unit first, Unit second) => true;
    public static bool operator !=(Unit first, Unit second) => false;
}

【讨论】:

  • 如果您在回答中解释为什么您认为这是一种比其他类似替代方法更可取的方法,例如定义void Wrapper(Action action) =&gt; Wrapper(() =&gt; { action(); return (object)null; });。另外,我会注意到类型推断应该注意你对Wrapper&lt;Unit&gt;()的调用:return Wrapper(() =&gt; { action(); return Unit.Default; });
  • 我喜欢这种改进,但我仍然想知道您如何看待编写一个全新的类型,这比仅使用 object 调用包装器的 Func&lt;T&gt; 版本更好。
  • 我认为这很有趣。在此之前我从未听说过单位类型,但我有兴趣尝试一下。是否可以在任何地方使用相同的单元类型(例如,我不需要为自己的实现进行调整)?
  • @TheGreatB3 - 是的,您可以在任何地方使用它。它只是一个只能是单个值的值类型。所有的空隙都一样。
  • @PeterDuniho - 让我想想如何最好地表达它。它实际上只是使用函数式编程的概念,但这有点陈词滥调。我会带着东西回来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-24
  • 2017-03-25
  • 1970-01-01
  • 2018-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多