【问题标题】:Lambda property value selector as parameterLambda 属性值选择器作为参数
【发布时间】:2013-05-21 18:20:47
【问题描述】:

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

【问题讨论】:

  • 只是好奇:testParameter 的用途是什么?好像什么都没做,,,

标签: c# vb.net linq lambda


【解决方案1】:
private string MyMethod(int testParameter, Func<MyObject, string> selector)
{
    return selector(_myObject);
}

使用Func委托时,最后一个参数是返回类型,前N-1个是参数类型。在这种情况下,selector 有一个 MyObject 参数,它返回一个 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

【讨论】:

  • 真的那么简单吗?!我现在觉得有点傻! Func声明中的string有什么意义?
  • @XN16 指定委托的返回类型。
  • @Servy 我认为如果我尝试访问不同类型的另一个属性,则必须对其进行修改?
  • @XN16 - 我添加了Func 类型参数的简要说明。
  • @XN16 好吧,MyMethod 返回一个string,所以如果委托不匹配,它将无法编译。为了能够允许任意选择器,您需要将 MyMethod 设为泛型,并为选择器和返回类型使用泛型参数。
【解决方案2】:

我将向您展示一种非常灵活的不同方法(请参阅底部的 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&lt;T, V&gt;,所以可以被.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 =&gt; d.Name),它会返回:

名称   代码
测试1   T
测试2   问
测试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 =&gt; d.Name),但它也允许您指定一个有条件,如x =&gt; x.FirstOrDefault(y =&gt; 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   问

因为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));
}

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

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

所以你不需要写x=&gt;x.FirstOrDefault(... condition ...)

Try it in DotNetFiddle

【讨论】:

    【解决方案3】:

    在 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
    

    【讨论】:

      【解决方案4】:
      class MyClass
      {
          private MyObject _myObject = new MyObject() { Name = "Test", Code = "T" };
      
          private string MyMethod(int testParameter, Func<MyObject, string> selector)
          {
              return selector(_myObject );
          }
      }
      

      【讨论】:

        【解决方案5】:

        您可以使用选择器的委托来做到这一点:

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

        【讨论】:

          【解决方案6】:

          您可能正在寻找 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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2019-09-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-03-28
            相关资源
            最近更新 更多