将多个参数传递给Func<>在C#中

时间:2017-06-13 14:19:34

标签: c# linq

这是来自编程测试,因此我确信有更好的方法可以做到这一点,但问题需要这个具体的答案。

我有一个方法结果,它简单地匹配谓词并返回一个bool,并且有问题的谓词检查一个字符串数组,以报告任何字符串是否超过长度为5。

static bool Result<T>(T[] values, Func<T, bool> predicate)
{
    if (values.Where<T>(predicate).Count() > 0)
        return true;
    else
        return false;
}

static bool StringLengthLessThan5(string str)
{
    return str.Length < 5;
}

最后这样使用 -

bool val2 = Result(new string[5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" }, StringLengthLessThan5);

这可以按预期工作,但是现在我需要使用相同类型的代码,但是参数化Func以允许字符串长度的动态值,即我的Func<string,bool>现在需要Func<string,int,bool> < / p>

static bool StringLengthLessThanGivenNumber(string str,int length)
{
    return str.Length < length;
}

或者,

static Func<string, int, bool> FuncForDynamicStringlength = (s, i) => s.Length < i;

我的问题是,本着保持新Func的调用代码相同的精神,即我仍然想使用 -

Result(new string[5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" }, StringLengthLessThanGivenNumber);

但是,我如何传递参数?

Func现在需要2个参数,第一个是字符串,第二个是int,如何传递第一个参数?我可以传递int长度来进行比较,但是我在第一个参数上难以接受。

我意识到我可以遍历我的字符串[]并在这样的foreach中调用Func(我知道在这个特定的实例中这没有意义,因为我覆盖了bool的值,但我和#39;我确定你得到了我的问题) -

foreach(string str in new string[5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" })
{
    bool val3_1 = StringLengthLessThanGivenNumber(str, 5);
}

但如果可能,我想使用我的原始代码。

3 个答案:

答案 0 :(得分:4)

您还可以查看partial function application。您可以编写另一个函数,该函数接收带有n参数的函数,并返回一个接受n-1参数的函数。在内部,新函数维护对在调用时已知的某些数据的引用。例如:

static Func<string, bool> PartialApply(Func<string, int, bool> action, int length)
{
    return (string str) => action(str, length);
}

在这里,您定义一个函数,它接受带有两个参数和布尔返回类型的Func。您将使用此函数创建一个只接受一个参数的新函数:

var stringLessThan6 = PartialApply(StringLengthLessThanGivenNumber, 6);

现在,您使用变量来引用新函数,而不是在类中编写(和命名)新函数。您可以即时生成此新功能。这允许您仅通过更改参数来创建不同的变体:

var stringLessThan1 = PartialApply(StringLengthLessThanGivenNumber, 1);
var stringLessThan10 = PartialApply(StringLengthLessThanGivenNumber, 10);
var stringLessThan1000 = PartialApply(StringLengthLessThanGivenNumber, 1000);

由于您现在拥有仅接受string并返回bool的功能,因此您可以使用预期谓词的那些功能:

bool val2 = Result(new string[5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" }, stringLessThan6);
bool val2 = Result(new string[5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" }, stringLessThan10);
bool val3 = Result(new string[5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" }, stringLessThan1000);

答案 1 :(得分:3)

这是你真正想要的:

var result4 = strings.Any(s => s.Length < 6);

无需新方法。但你正在做的事情是值得的。

所以这里是你问题的确切答案,只有一些补充。

  1. 除非您的方法需要某个特定原因,否则不要求参数为数组。这个没有。 IEnumerable<T>就是我们所需要的,所以我们接受这一点。一个string数组实现IEnumerable<string>,因此我们可以接受一个数组。
  2. Count()可能需要完成整个序列,具体取决于传入的内容。如果你要问的是它是否为0,则无需这样做。 Any()会在谓词第一次找到匹配项时停止并返回true。它也更简单,名称说明了它在做什么。它不像s.Where(p).Count() > 0
  3. 让它成为一种扩展方法。它们非常方便,他们预期,因为我们在这里所做的是Linq,你仍然可以将它用作常规方法,即s.Any(p)f(a, n, p)编译器都很好。
  4. 行:

    a.f(n, p)

    因此:

    static bool Result<T1, T2>(this IEnumerable<T1> values, T2 param, Func<T1, T2, bool> predicate)
    {
        return values.Any(t1 => predicate(t1, param));
    }
    
    static bool StringLengthLessThanGivenNumber(string str, int len)
    {
        return str.Length < len;
    }
    

    这有效,但它不灵活。有些情况可能有意义:可能是基于表单中的用户输入进行过滤。谓词来自一个选择,参数来自其他地方。

    但是,一般情况下,您最好将var strings = new string[] { "asdf", "a", "asdsffsfs", "wewretete", "adcv", "planxty" }; var result = strings.Result(6, StringLengthLessThanGivenNumber); 作为谓词的一部分传递,就像您最初使用它一样。看看Kenneth K对于清理这种方法的经典方法的出色答案,我应该记住这一点。

    param

    ...

    static bool Result2<T1>(this IEnumerable<T1> values, Func<T1, bool> predicate)
    {
        return values.Any(predicate);
    }
    

    或者更好:

    var result2 = strings.Result2(s => StringLengthLessThanGivenNumber(s, 6));
    

答案 2 :(得分:2)

将谓词包装成lambda表达式并使用变量或常量传递所需的长度,如下所示:

int len = 5;
bool val1 = Result(new string[5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" }, x => StringLengthLessThanGivenNumber(x, len));
bool val2 = Result(new string[5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" }, x => StringLengthLessThanGivenNumber(x, 5));

解决此问题的另一种方法是为string集合定义扩展方法。

public static class StringCollectionExtensions
{
    public static bool HasLengthLessThan(this IEnumerable<string> collection, int length)
    {
        return collection.Any(x => x.Length < length);
    }
}

然后你可以像这样使用它:

var testStrings = new string[5] {"asdf", "a", "asdsffsfs", "wewretete", "adcv"};

bool val3 = testStrings.HasLengthLessThan(6);
相关问题