C# 如何传递函数<;T>;具有可变数量的参数

C# 如何传递函数<;T>;具有可变数量的参数,c#,func,C#,Func,所以我试图通过一个Func传递一个参数数目可变的函数 // No arguments var result = GetValue("Bob", () => { return "Bob Smith"; }); // 1 argument var i = ...; var result = GetValue("Joe", () => { return "Joe " + i.ToString(); }); // 2 arguments var i = ...; var e = ...;

所以我试图通过一个Func传递一个参数数目可变的函数

// No arguments
var result = GetValue("Bob", () => { return "Bob Smith"; });

// 1 argument
var i = ...;
var result = GetValue("Joe", () => { return "Joe " + i.ToString(); });

// 2 arguments
var i = ...;
var e = ...;
var result = GetValue("Henry", () => { 
    return $"i: {i.ToString()}, e: {e.ToString()}"; 
});
比如:

public object GetValue<T>(string name, Func<object> func) { 
    var result = func.DynamicInvoke();
}
现在不需要超过2个参数。。但可能是在未来。调用语法对我来说是最重要的一点。我不想让打电话的人投任何东西

我已经看过了,但它们似乎都需要一些调用语法,我不想使用

你知道如何做到这一点吗?

答案是不要。首先,您试图调用一个不带参数并返回某个对象的方法。您不能只是“生成”一个需要此类型参数的函数,否则您将如何使用所需参数调用它

// No arguments
var result = GetValue("Bob", () => { return "Bob Smith"; });

// 1 argument
var i = ...;
var result = GetValue("Joe", () => { return "Joe " + i.ToString(); });

// 2 arguments
var i = ...;
var e = ...;
var result = GetValue("Henry", () => { 
    return $"i: {i.ToString()}, e: {e.ToString()}"; 
});
您已经在创建lambda,您需要关闭您想要的“参数”,这样您就可以有效地添加其他参数

// No arguments
var result = GetValue("Bob", () => { return "Bob Smith"; });

// 1 argument
var i = ...;
var result = GetValue("Joe", () => { return "Joe " + i.ToString(); });

// 2 arguments
var i = ...;
var e = ...;
var result = GetValue("Henry", () => { 
    return $"i: {i.ToString()}, e: {e.ToString()}"; 
});
否则,如果确实要传入任何具有任意数量参数的委托,请将参数设置为delegate,但必须提供委托的确切类型,并且必须为调用提供参数

public object GetValue<T>(string name, Delegate func, params object[] args) { 
    var result = func.DynamicInvoke(args);
}
public object GetValue(字符串名、委托函数、参数对象[]args){
var结果=函数DynamicInvoke(args);
}
var result=GetValue(“Bob”,newfunc(()=>{return“Bob-Smith”;}));
//1参数
var result=GetValue(“Joe”,新函数((i)=>{return“Joe”+i.ToString();}),argI);
//2个论点
var result=GetValue(“亨利”,新函数((i,e)=>{
返回$“i:{i.ToString()},e:{e.ToString()}”;
}),argI,argE);

据我所知,您希望能够忽略部分参数,以防调用方不需要它们。但是,我认为您应该从决定如何调用代理开始。请注意,DynamicVoke的参数数量必须与实际方法的参数数量相匹配,因此,如果指定的参数多于所接受的参数,则它不会适用于所传递的所有委托

除非使用反射和发射来调用委托,否则必须使用重载。你制作它们的方式取决于这些参数是否应该是“懒惰的”。如果是这样的话,每个过载的主体将与其他过载主体有很大的不同,以证明其存在的合理性。如果不是,它是这样的:

object GetValue(Func<int, object> f)
{
    return GetValue((i,s) => f(i));
}

object GetValue(Func<int, string, object> f)
{
    return f(1, "0");
}
var result0 = GetValue("Bob", (Func<object>)(() => { return "Bob Smith"; }));
var result1 = GetValue("Joe", (Func<object, object>)((i) => { return "Joe " + i.ToString(); }));
var result2 = GetValue("Henry", (Func<object, object, object>)((i, e) =>
{
    return $"i: {i.ToString()}, e: {e.ToString()}";
}));
对象获取值(Func f)
{
返回GetValue((i,s)=>f(i));
}
对象GetValue(函数f)
{
申报表f(1,“0”);
}
我认为这不是一个好的解决方案,因为第一个重载表明只生成第一个参数,而在引擎盖下,所有参数都通过了。在这种情况下,最好将希望从该方法获得的所有信息封装在一个类中,并传递一个实例。如果使用
Lazy
,这也适用于第一种情况,但代码会变得更复杂


与您的问题相关的另一件事是:
delegate{…}
语法,它可以转换为具有任何参数的委托类型,然后被忽略。但是,这种情况发生在调用站点。

首先,您正在破坏静态类型的目的:在静态类型中,关键是在编译时您可以准确地知道某个对象的类型,因此编译器将允许您执行某些操作。这里您期望的是动态键入通常提供的功能,您可以传递“某物”,然后在运行时找出如何使用它

你应该重新考虑你的这个需求,看看你是否能用一种更好的静态类型的方法来解决这个问题


也就是说,有一种不太好的方法可以使它在静态上保持正常,即使用自定义类型和一些隐式类型转换(发生在编译时!):

public object GetValue(字符串名,DynamicFunc func)
{
返回函数DynamicInvoke(“a”、“b”);
}
公共类动态函数
{
public Func None{get;private set;}
公共Func One{get;私有集;}
公共Func二{get;私有集;}
公共对象DynamicInvoke(对象参数1=null,对象参数2=null)
{
如果(两个!=null)
返回两个(param1,param2);
else if(一!=null)
返回一个(参数1×参数2);
else if(无!=null)
返回None();
返回null;
}
公共静态隐式运算符DynamicFunc(Func Func)
=>新的DynamicFunc{None=func};
公共静态隐式运算符DynamicFunc(Func Func)
=>新的DynamicFunc{One=func};
公共静态隐式运算符DynamicFunc(Func Func)
=>新的DynamicFunc{Two=func};
}
然后你可以这样使用它:

object GetValue(Func<int, object> f)
{
    return GetValue((i,s) => f(i));
}

object GetValue(Func<int, string, object> f)
{
    return f(1, "0");
}
var result0 = GetValue("Bob", (Func<object>)(() => { return "Bob Smith"; }));
var result1 = GetValue("Joe", (Func<object, object>)((i) => { return "Joe " + i.ToString(); }));
var result2 = GetValue("Henry", (Func<object, object, object>)((i, e) =>
{
    return $"i: {i.ToString()}, e: {e.ToString()}";
}));
var result0=GetValue(“Bob”,(Func)(()=>{return“Bob Smith”;}));
var result1=GetValue(“Joe”,(Func)((i)=>{return“Joe”+i.ToString();}));
var result2=GetValue(“亨利”,“函数”((i,e)=>
{
返回$“i:{i.ToString()},e:{e.ToString()}”;
}));
请注意,您需要为lambda表达式指定一个显式类型,因为否则编译器将无法确定该类型

这看起来好还是容易理解?我不这么认为。如果您想要正确的静态类型,只需在这里使用方法重载。这样,您也不需要动态调用函数,这也使得调用函数实际上更加容易。

只提供一个参数,但要使其成为复杂类型 如果您有一个详细的变量列表,这些变量可能是传入的委托所需要的,也可能不是传入的委托所需要的,那么您可以将它们作为单个参数提供,可能命名为
CallingContext

class CallingContext
{
    private CallingContext(string name, int index)
    {
        Name = name;
        Index = index;
    }
    public string Name { get; private set; }
    public int Index { get; private set; }
}

public TOut GetValue<TOut>(string name, Func<CallingContext,TOut> func) { 
    var i = LookupIndex(name);
    var context = new CallingContext(name, i);        
    return func(context);
}
将来,如果必须添加其他参数,只需将它们添加到CallingContext类中即可。如果您希望类变得相当丰富,请考虑将其暴露为接口,并使用“代码> FUNC”的委托签名,以便可以对单元测试进行SIMM和存根。还有,如果有任何论点
public delegate T ParamsDelegate<T>(params Object[] args);

public T GetValue<T>(ParamsDelegate<T> f, params Object[] args)
{
    return f(args);            
}

// 0 passed argument values.  
String s0 = GetValue(args => "Bob Smith");

// 1 argument.
String s1 = GetValue(
    args => String.Format("{0} Smith", args),
    "Bob"
    );

// 2 arguments.            
String s2 = GetValue(
    args => String.Format("{0} {1}", args),
    "Bob", "Smith"
    );
String s0 = GetValue(args => "Bob Smith");
String s0 = GetValue(_ => "Bob Smith");