传递和执行具有不同参数签名的函数

时间:2015-09-04 09:22:49

标签: c# generics functional-programming currying

我试图以一种相当通用的方式构造一些对象。一些对象有构造函数参数,其他对象没有。

我想要实现的是返回某种构建器函数,如果需要,我可以提供构造函数参数。

我知道我可以传递可选参数,但在我的实际情况中,有几个层,我讨厌在层次结构中添加可选参数。

我对部分申请/讨论不太满意,但我可以在这里使用,如果是,怎么样?

这里有一些示例代码 - 它们不会起作用 - 试图解释我之后的更多内容。

public void Main()
{
    dynamic buildClass = ClassBuilder<BaseClass>(true);
    // ideally I'd like to be able to supply the constructor data 
    // here
    var theClass = buildClass(???)

} 

public Func<???, TClass> ClassBuilder<TClass>(bool flag) where TClass : BaseClass
{
    // obviously this won't work since the delegates have different
    // signatures
    if (flag) return () => GetClassA();
    return (x) => GetClassB(x);
}


public object GetClassA()
{
    return new ClassA();
}

public object GetClassB(string param)
{
    return new ClassB(param);
}

public class BaseClass {}

public class ClassA : BaseClass {}

public class ClassB : BaseClass
{
    private string _param;
    public ClassB(string param)
    {
        _param = param;
    }
}

很多thx

取值

3 个答案:

答案 0 :(得分:2)

要详细说明@Sylwekqaz,您可以使用下面的内容,而不是限制自己BaseClass的类型。

public static class Builder
{
    public static T Build<T>(params object[] args) where T : class
    {
        var info = typeof(T).GetConstructor(args.Select(arg => arg.GetType()).ToArray());
        if (info == null)
            throw new ArgumentException(@"Can't get constructor :(", "args");

        return (T)info.Invoke(args.ToArray());
    } 
}

然后你可以将你的建设者称为

var a = Builder.Build<ClassA>();
var b = Builder.Build<ClassB>(); // need parameterless ctor in ClassB
var c = Builder.Build<ClassB>("param");

答案 1 :(得分:1)

您必须使用代码反射来检测带有参数的构造函数/方法并调用它。

Type type = typeof(YourClass);
ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string) });
object instance = ctor.Invoke(new object[] { 10 });

〜来源:Using C# reflection to call a constructor

如果必须使用方法GetClassX

,则可以使用MethodInfo类

更多信息

https://msdn.microsoft.com/en-us/library/system.type.getconstructor%28v=vs.110%29.aspx

https://msdn.microsoft.com/en-us/library/system.reflection.constructorinfo.invoke%28v=vs.110%29.aspx

答案 2 :(得分:1)

我并不完全遵循您的代码示例,但您会询问部分应用和currying ......

我发现的最好的方法是创建N个函数,这些函数取自1-N通用参数,然后让编译器选择你想要的那个。如果您查看我的language-ext项目,我有两个函数,一个名为curry,一个名为par,用于currying和部分应用:

Currying source

Partial application source

要部分申请,请执行以下操作:

// Example function
int AddFour(int a,int b,int c, int d)
{
    return a + b + c + d;
}

// This returns a Func<int,int,int> with the first two arguments 10 & 5 auto-provided
var tenfive = par(AddFour, 10, 5);

// res = 10 + 5 + 1 + 2
var res = tenfive(1,2);

要咖喱,请这样做:

// Example function
int AddFour(int a,int b,int c, int d)
{
    return a + b + c + d;
}

// Returns Func<int,Func<int,Func<int,Func<int,int>>>>
var f = curry(AddFour);

// res = 10 + 5 + 1 + 2
var res = f(10)(5)(1)(2);