我如何制作一个通用的包装方法,以返回包装方法的结果(如果有的话)?

时间:2019-07-10 21:54:04

标签: c# generics methods delegates

我正在尝试创建一个接受另一个方法的方法,并返回内部方法返回的值(如果该内部方法不无效)。我想做到这一点而又不区分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
}

2 个答案:

答案 0 :(得分:0)

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

每当遇到这种情况时,我都会编写一个返回类型为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<T>版本必须创建一个新的委托实例并捕获一个局部变量,这会产生一些开销,但是至少当我必须要做这种事情时,包装逻辑和包装逻辑非常复杂,以至于Lambda的开销无关紧要。

答案 1 :(得分:0)

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

通过实现Unit类型(即单个值struct),您可以将Action转换为Func<Unit>。实际上,这只是使用适配器模式。

现在,您正在处理Func<T>形式的一种委托。然后,您可以将所有艰苦的工作投入到单个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;
}