作为参数的 Lambda 属性值选择器

本文关键字:属性 选择器 Lambda 参数 | 更新日期: 2023-09-27 18:14:54

>我需要修改一个方法,以便它有一个额外的参数,该参数将采用一个将在内部对象上使用的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 ???;
    }
}

我想这样称呼它:

string result = _myClassInstance.MyMethod(1, (x => x.Name));

或:

string result = _myClassInstance.MyMethod(1, (x => x.Code));

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

任何帮助将不胜感激,也是 VB.NET 解决方案的额外加分,不幸的是,最终实施需要在我们单独的 VB 项目中

作为参数的 Lambda 属性值选择器

private string MyMethod(int testParameter, Func<MyObject, string> selector)
{
    return selector(_myObject);
}

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

您可以像以下方式调用它:

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

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

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

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

Public Function MyMethod(testParameter as Integer, selector as Func(Of MyObject, String))
    Return selector(_myObject)
End Function

通用版本将是:

Public Function MyMethod(Of T)(testParameter as Integer, selector Func(Of MyObject, T))
    Return selector(_myObject)
End Function

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

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

用法(示例(:

var myQuery=(from x in Customers select x).MyDistinct(d => d.CustomerID);

在此示例中,查询按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());
    }
}

您可以看到 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"}
};

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

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

哪个会返回

名称   代码
测试1   T
测试2   Q

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

名称   代码
测试1   T
测试2   Q
测试5   Q

请注意,由于MyDistinct是用泛型TV声明的,因此它会自动识别和使用正确的对象类型并返回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);
    }
}

此修改允许您完全像以前一样使用它,即通过指定一个参数(如 .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"))
        );

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

名称   代码
测试1   T
测试2   Q

由于Test5不满足条件(它不包含 1 或 2(,因此您在第 3 行中得到 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));
}

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

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

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

在DotNetFiddle中尝试一下

在 C# 中

您要查找的参数类型 函数

private string MyMethod(int testParameter, Func<MyClass,string> selector){
    return selector(_myObject);
}

在 VB 中,您仍然希望 Func 语法略有不同。

Function MyMethod(ByVal testParameter As Integer, ByVal selector as Func(Of MyClass,string) as string
    return selector(_myObject)
End Function
class MyClass
{
    private MyObject _myObject = new MyObject() { Name = "Test", Code = "T" };
    private string MyMethod(int testParameter, Func<MyObject, string> selector)
    {
        return selector(_myObject );
    }
}

您可以使用选择器的委托来执行此操作:

delegate string SampleDelegate(MyObject obj);
private string MyMethod(int testParameter, SampleDelegate selector)
{
    return selector(_myObject);
}
您可能

正在寻找 Delegate 类(VB 中的"Delegate",C# 中的"delegate"(或其子类型之一。

此页面有一些您可能会发现有用的示例,尤其是在页面底部附近。

以下是您想要执行的操作的 VB 示例:

Public Class MyClass
  Private Property _myObject As MyObject = New MyObject With {.Name = "Test", .Code = "T"}
  Private Function MyMethod(testParameter As Integer, selector As Func(Of MyObject, String)) As String
    Return selector(_myObject).ToString
  End Function
End Class