Lambda属性值选择器作为参数

时间:2013-05-21 18:20:47

标签: c# vb.net linq 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 ???;
    }
}

我想称之为:

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

或:

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

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

任何帮助都会受到赞赏,也是VB.NET解决方案的额外奖励点,不幸的是最终的实现需要在我们唯一的VB项目中!

6 个答案:

答案 0 :(得分:35)

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

答案 1 :(得分:3)

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

将返回

  

名称代码的   
Test1 T   
Test2 Q

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

  

名称代码的   
Test1 T   
测试2问   
Test5 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"))
        );

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

  

名称代码的   
Test1 T   
测试2问   
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 ...)

答案 2 :(得分: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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

您可以使用选择器的代表执行此操作:

delegate string SampleDelegate(MyObject obj);

private string MyMethod(int testParameter, SampleDelegate selector)
{
    return selector(_myObject);
}

答案 5 :(得分:0)

您可能正在寻找Delegate类(VB中的“Delegate”,C#中的“delegate”)或其子类型之一。

This page有一些您可能会觉得有用的示例,尤其是在页面底部附近。

以下是您想要做的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