C#方法,无论输入/返回类型如何,都将另一种方法作为参数

时间:2019-08-22 20:55:10

标签: c# functional-programming

很抱歉,标题不清楚。基本上,我想通过将许多重复的错误处理移到一个位置来干燥我的代码。

我正在调用几种方法,所有方法都会引发类似的错误。每个参数采用不同的参数,并返回不同的类型。我希望能够执行以下操作:

public class MyClass {
    public static ErrorWrapper<Void> Method1(string s1, string s2) {
        return Wrapper<Void>(System.Method1, s1, s2);
    }

    public static ErrorWrapper<string> Method2(string s) {
        return Wrapper<string>(System.Method2, s);
    }

    public static ErrorWrapper<MyOtherClass> Method3(string s, int i) {
        return Wrapper<MyOtherClass>(System.Method3, s, i)
    }

    private static ErrorWrapper<T> Wrapper<T>(Func f, /*parameters?*/) {
        try {
            return f(parameters);
        } catch {
            // Handle exceptions
        }
}

之所以需要这样做,是因为我编写的语言绑定没有异常处理,因此使用错误包装类是安全地调用标准库方法的唯一方法。

2 个答案:

答案 0 :(得分:2)

除非我在这里缺少任何内容-

private static ErrorWrapper<T> Wrapper<T>(Func<T> f) 
{
    // implementation
}

用法:

return Wrapper<string>(() => System.Method2(s));

return Wrapper<MyOtherClass>(() => System.Method3(s, I));

答案 1 :(得分:0)

这是我对您正在尝试的理解的建议。不幸的是,您必须使用慢的DynamicInvoke才能在调用常规Delegate时在运行时检查类型。您不能使用void作为泛型的类型参数,因此我创建了一个我专门处理的MyVoid类。

我更新了答案,以反映System.Method是实际方法,而不是静态字段,因此在传递给Wrapper<>时必须将其强制转换-C#无法将方法转换为一般Delegate

我还添加了类型安全的版本,但是需要创建许多样板方法来处理参数数量,但是它确实消除了很多强制转换并且可以静态调用委托,这应该更高效。

我不小心使Method3最初使用了两个字符串参数,并且非类型安全版本直到运行时调用才出现错误。类型安全版本在编译时遇到了错误。

public class MyVoid { }

public class ErrorWrapper<T> {
    public T Result;
    public bool ValidResult;
    public Exception Exception;

    public ErrorWrapper(T res) {
        ValidResult = true;
        Result = res;
    }

    public ErrorWrapper(Exception e) {
        Exception = e;
        ValidResult = false;
    }

    public ErrorWrapper() { //  void
        ValidResult = true;
    }
}

public class MyClass {
    public static ErrorWrapper<MyVoid> Method1(string s1, string s2) => Wrapper<MyVoid>((Action<string, string>)System.Method1, s1, s2);
    public static ErrorWrapper<string> Method2(string s) => Wrapper<string>((Func<string, string>)System.Method2, s);
    public static ErrorWrapper<MyOtherClass> Method3(string s, int i) => Wrapper<MyOtherClass>((Func<string, int, MyOtherClass>)System.Method3, s, i);

    private static ErrorWrapper<T> Wrapper<T>(Delegate f, params object[] p) {
        try {
            switch (default(T)) {
                case MyVoid _:
                    f.DynamicInvoke(p);
                    return new ErrorWrapper<T>();
                default:
                    return new ErrorWrapper<T>((T)f.DynamicInvoke(p));
            }
        }
        catch (Exception e) {
            // Handle exceptions
            return new ErrorWrapper<T>(e);
        }
    }
}

public static class ErrorWrapper {
    public static ErrorWrapper<T> New<T>(T res) => new ErrorWrapper<T>(res);
}

public class MyTypeSafeClass {
    public static ErrorWrapper<MyVoid> Method1(string s1, string s2) => WrapperAction(System.Method1, s1, s2);
    public static ErrorWrapper<string> Method2(string s) => WrapperFunc(System.Method2, s);
    public static ErrorWrapper<MyOtherClass> Method3(string s, int i) => WrapperFunc(System.Method3, s, i);

    private static ErrorWrapper<MyVoid> WrapperAction<T1, T2>(Action<T1, T2> f, T1 p1, T2 p2) {
        try {
            f(p1, p2);
            return ErrorWrapper.New(default(MyVoid));
        }
        catch (Exception e) {
            // Handle exceptions
            return new ErrorWrapper<MyVoid>(e);
        }
    }

    private static ErrorWrapper<TRes> WrapperFunc<T1, TRes>(Func<T1, TRes> f, T1 p1) {
        try {
            return ErrorWrapper.New(f(p1));
        }
        catch (Exception e) {
            // Handle exceptions
            return new ErrorWrapper<TRes>(e);
        }
    }

    private static ErrorWrapper<TRes> WrapperFunc<T1, T2, TRes>(Func<T1, T2, TRes> f, T1 p1, T2 p2) {
        try {
            return ErrorWrapper.New(f(p1, p2));
        }
        catch (Exception e) {
            // Handle exceptions
            return new ErrorWrapper<TRes>(e);
        }
    }
}