Lambda属性值选择器作为参数

XN1*_*N16 26 c# linq vb.net lambda

我需要修改一个方法,以便它有一个额外的参数,该参数将采用将在内部对象上使用的lambda表达式来返回给定属性的值.原谅我可能错误地使用术语,因为这是我第一次涉足LINQ表达式!

我试图寻找答案,但正如我所提到的,我的术语似乎已经关闭,我可以找到的例子太复杂或者处理集合函数的表达式,比如.Where()我熟悉的.

到目前为止我所拥有的(减少版本):

class MyClass
{
    private MyObject _myObject = new MyObject() { Name = "Test", Code = "T" };

    private string MyMethod(int testParameter, ??? selector)
    {
        //return _myObject.Name;
        //return _myObject.Code;
        return ???;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想称之为:

string result = _myClassInstance.MyMethod(1, (x => x.Name));
Run Code Online (Sandbox Code Playgroud)

要么:

string result = _myClassInstance.MyMethod(1, (x => x.Code));
Run Code Online (Sandbox Code Playgroud)

显然,我缺少的部分是selector参数MyMethod,如何将它应用于局部变量以及在调用时如何将必需的属性传递给方法.

任何帮助将不胜感激,也是VB.NET解决方案的额外奖励点,不幸的是最终的实现需要在我们唯一的VB项目中!

Lee*_*Lee 36

private string MyMethod(int testParameter, Func<MyObject, string> selector)
{
    return selector(_myObject);
}
Run Code Online (Sandbox Code Playgroud)

使用Func委托时,最后一个参数是返回类型,第一个N-1是参数类型.在这种情况下,只有一个MyObject参数selector,它返回一个string.

您可以像下面这样调用它:

string name = _myClassInstance.MyMethod(1, x => x.Name);
string result = _myClassInstance.MyMethod(1, x => x.Code);
Run Code Online (Sandbox Code Playgroud)

由于返回类型MyMethod与您的selector委托的返回类型匹配,因此您可以将其设为通用:

private T MyMethod<T>(int testParameter, Func<MyObject, T> selector)
{
    MyObject obj = //
    return selector(obj);
}
Run Code Online (Sandbox Code Playgroud)

编辑:我不知道VB.Net但看起来它会是:

Public Function MyMethod(testParameter as Integer, selector as Func(Of MyObject, String))
    Return selector(_myObject)
End Function
Run Code Online (Sandbox Code Playgroud)

通用版本将是:

Public Function MyMethod(Of T)(testParameter as Integer, selector Func(Of MyObject, T))
    Return selector(_myObject)
End Function
Run Code Online (Sandbox Code Playgroud)


Mat*_*att 5

我将向您展示非常灵活的另一种方法(请参阅底部的DotNetFiddle):您可以轻松地编写自己的LINQ函数来扩展现有功能或编写自己的函数,并从LINQ查询的功能中受益。

在此示例中,我Distinct以某种方式改进了Linq的功能,以便您可以指定用于分组的字段。

用法(示例):

var myQuery=(from x in Customers select x).MyDistinct(d => d.CustomerID);
Run Code Online (Sandbox Code Playgroud)

在此示例中,查询按分组,CustomerID并返回每个组的第一个元素。

声明MyDistinct

public static class Extensions
{
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query, 
                                                    Func<T, V> f)
    {
        return query.GroupBy(f).Select(x=>x.First());
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到f第二个参数被声明为Func<T, V>,因此该.GroupBy语句可以使用它。


返回您问题中的代码(如果已声明)

class MyObject
{
    public string Name;
    public string Code;
}

private MyObject[] _myObject = {
    new MyObject() { Name = "Test1", Code = "T"},
    new MyObject() { Name = "Test2", Code = "Q"},
    new MyObject() { Name = "Test2", Code = "T"},
    new MyObject() { Name = "Test5", Code = "Q"}
};
Run Code Online (Sandbox Code Playgroud)

您可以将其与新定义的函数一起使用,MyDistinct如下所示:

var myQuery = (from x in _myObject select x).MyDistinct(d => d.Code);
Run Code Online (Sandbox Code Playgroud)

它将返回

名称代码
Test1 T
Test2 Q

或者您可以.MyDistinct(d => d.Name)在查询中使用它返回:

名称代码
Test1 T
Test2 Q
Test5 Q

请注意,因为MyDistinct是使用泛型T和声明的V,所以它会自动识别并使用正确的对象类型并返回MyObject元素。


高级用法

注意,MyDistinct始终采用每个组的第一个元素。如果您需要一个条件来定义所需的元素怎么办?

您可以按照以下方法进行操作:

public static class Extensions
{
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query,
                                                    Func<T, V> f, 
                                                    Func<IGrouping<V,T>,T> h=null)
    {
        if (h==null) h=(x => x.First());
        return query.GroupBy(f).Select(h);
    }
}
Run Code Online (Sandbox Code Playgroud)

此修改允许您完全像以前一样使用它,即通过指定一个参数(例如).MyDistinct(d => d.Name),但也可以指定一个具有条件的条件(例如x => x.FirstOrDefault(y => y.Name.Contains("1")||y.Name.Contains("2"))第二个参数),例如:

var myQuery2 = (from x in _myObject select x).MyDistinct(d => d.Name,
        x=>x.FirstOrDefault(y=>y.Name.Contains("1")||y.Name.Contains("2"))
        );
Run Code Online (Sandbox Code Playgroud)

如果运行此查询,则结果为:

名称代码
Test1 T
Test2 Q
null

由于Test5不符合条件(不包含1或2),因此在第三行中您将获得null

注意:如果只想公开条件,则可以通过将其实现为以下条件来使其更简单:

public static IEnumerable<T> MyDistinct2<T, V>(this IEnumerable<T> query,
                                                Func<T, V> f,
                                                Func<T,bool> h=null
                                                )
{
    if (h == null) h = (y => true);
    return query.GroupBy(f).Select(x=>x.FirstOrDefault(h));
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,查询将如下所示:

var myQuery3 = (from x in _myObject select x).MyDistinct2(d => d.Name,
                    y => y.Name.Contains("1") || y.Name.Contains("2")
                    );
Run Code Online (Sandbox Code Playgroud)

所以你不需要写x=>x.FirstOrDefault(... condition ...)

在DotNetFiddle中尝试