30

我需要修改一个方法,使其具有一个额外的参数,该参数将采用一个 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));

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

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

4

6 回答 6

40
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
于 2013-05-21T18:26:13.217 回答
5

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

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

用法(示例):

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);

这将返回

名称 代码
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);
    }
}

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

如果你运行这个查询,结果是:

名称 代码
Test1 T
Test2 Q
null

因为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 中尝试

于 2014-07-25T09:20:05.843 回答
2

在 C# 中

您要查找的参数类型 Func

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
于 2013-05-21T18:35:23.130 回答
0
class MyClass
{
    private MyObject _myObject = new MyObject() { Name = "Test", Code = "T" };

    private string MyMethod(int testParameter, Func<MyObject, string> selector)
    {
        return selector(_myObject );
    }
}
于 2013-05-21T18:28:22.633 回答
0

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

delegate string SampleDelegate(MyObject obj);

private string MyMethod(int testParameter, SampleDelegate selector)
{
    return selector(_myObject);
}
于 2013-05-21T18:29:57.707 回答
0

您可能正在寻找 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
于 2013-05-21T18:43:39.300 回答