【问题标题】:Pass multiple arguments to a Func<> in C#将多个参数传递给 C# 中的 Func<>
【发布时间】:2017-06-13 14:19:33
【问题描述】:

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

我有一个 Result 方法,它简单地匹配一个谓词并返回一个布尔值,并且该谓词检查一个字符串数组以报告是否有任何字符串长度超过 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&lt;string,bool&gt; 现在需要为 Func&lt;string,int,bool&gt;

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 长度进行比较,但我被第一个参数难住了。

我意识到我可以循环遍历我的 string[] 并像这样在 foreach 中调用 Func(我知道这在这个特定实例中没有意义,因为我正在覆盖 bool 的值,但我确定你明白我的问题) -

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

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

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    只需将谓词包装成 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);
    

    【讨论】:

    • 感谢您指出扩展方法的使用,但就像我告诉 Ed 一样,我对您的第一篇文章更感兴趣 - bool val2 = Result(new string[5] { "asdf" , "a", "asdsffsfs", "wewretete", "adcv" }, x => StringLengthLessThanGivenNumber(x, 5)); with - static bool StringLengthLessThanGivenNumber(string str,int length) { return str.Length
    • @rakesh 刚试过,它编译得很好。您的代码还有其他问题。你可以在这里看到它的实际效果:rextester.com/CTPBX83730 只需点击底部的运行按钮。
    • 对不起 - 我的错误,我使用带有 2 个输入参数的 Func,您的代码编译得很好。
    • 您的代码引出了另一个问题,我会在这里问是否可以。我修改了您的代码 - rextester.com/edit/XAWWQS51026,但我不明白静态 bool StringLengthGreaterThanGivenNumber(string str, int length, int length2, int length3) 如何满足 Func 谓词。参数数量显然不匹配。
    • 因为它被包裹在 lambda 中,它本身就是 Func 。并且该 lambda 调用传递所有必需参数的其他方法。
    【解决方案2】:

    您也可以查看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);
    

    【讨论】:

    • 有趣,没听说过PartialApply,让我读一读。
    • 我读了一些书,对你的感激不尽。我的工具箱中有一个新工具,我希望我可以将所有这些解决方案标记为答案,即使我无法标记您的解决方案,但请知道您对我表示感谢。
    【解决方案3】:

    这是你真正想要的:

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

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

    以下是您问题的确切答案,并添加了一些内容。

    1. 永远不要要求参数是数组,除非您的方法出于某些特殊原因需要它是数组。这个没有。 IEnumerable&lt;T&gt; 是我们所需要的,所以我们接受它。 string 的数组实现了IEnumerable&lt;string&gt;,所以我们可以接受一个数组。
    2. Count() 可能需要遍历整个序列,具体取决于传入的内容。如果您要问的只是它是否为零,则无需这样做。 Any() 在谓词第一次找到匹配项时停止并返回 true。它也更简单,名称说明了它在做什么。并不是s.Where(p).Count() &gt; 0s.Any(p) 更难阅读,但是在大型代码库中,每一点都很重要。
    3. 让我们也让它成为一种扩展方法。它们很方便,因为我们在这里所做的是 Linq,所以它们很方便,您仍然可以将它用作常规方法,即 f(a, n, p)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);
    

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

    不过,一般来说,您最好将param 作为谓词的一部分传递,就像您最初拥有它的方式一样。有关清理这种方法的经典方法,请参阅 Kenneth K 的出色答案,我应该记住这一点。

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

    ...

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

    或者更好:

    var result3 = strings.Result2(s => s.Length < 6);
    

    【讨论】:

    • 我只是想问你这些是扩展方法,我想我知道如何使用扩展方法解决这个问题,但我仍然想知道我是否可以使用 - Result(new字符串 [5] { "asdf", "a", "asdsffsfs", "wewretete", "adcv" }, StringLengthLessThanGivenNumber);以具有正确参数集的某种形式
    • LengthLessThanextension 方法会更干净:)
    • @KasparsOzols 是的!将其添加到您的答案中!
    • @Rakesh 您必须以某种方式传入“给定数字”。我给了你两种方法来做到这一点。
    • @Ed - 除了使用扩展方法或执行 foreach 并单独传递字符串之外,我试图做的事情并不是直接可行的。
    猜你喜欢
    • 1970-01-01
    • 2019-11-03
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    相关资源
    最近更新 更多