【问题标题】:Trouble with anonymous method匿名方法的问题
【发布时间】:2013-02-04 19:51:34
【问题描述】:

我有一个方法,除了 IEnumerable<T> 和一个 lambda 表达式,它描述了用于将 linq-to-sql 集合与数组进行比较的字段。该方法返回匹配的记录。

public IEnumerable<ZipCode> match<T>(IEnumerable<T> values, 
        Func<ZipCode, T> matchPhrase) {
    return (from zipCode in _table
            where values.Contains<T>(matchPhrase)
            select zipCode).Distinct();
}

我收到了错误:

参数类型'Func&lt;ZipCode, T&gt;' 不能分配给参数类型'T'

该方法会这样调用(其中valuesIEnumerable&lt;string&gt;x.zipcodestring):

var zipCodes = _zipCodeRepository.match(values, x => x.zipcode)

更新

根据 John 关于使用 HashSet&lt;T&gt; 的建议,我已经更改了代码,但是现在出现了不同的错误

方法 'System.Object DynamicInvoke(System.Object[])' 不支持 SQL 转换。

我认为我的问题可能不清楚,并且我认为我使用了错误的方法签名来获得我想要的结果。让我用一个更简单的代码示例来解释:

public IEnumerable<ZipCode> match(IEnumerable<string> values) {
    return (from zipCode in _table
            where values.Contains(zipCode.zipcode)
            select zipCode).Distinct();
}

我很想做到这一点,但使用匿名类型。我想通过 lambda 传递要在 Contains() 中使用的字段。所以zipCode.zipcode 将作为第二个参数传递给方法:x =&gt; x.zipcode

【问题讨论】:

  • 您几乎肯定希望在开始时将values 放入HashSet,以便更有效地搜索它。因为您正在枚举values,对_table 中的每个项目进行线性搜索。这既非常低效,又要多次迭代可枚举,在这样的函数中确实应该避免这种情况。

标签: c# linq-to-sql lambda anonymous-types


【解决方案1】:

我怀疑你想打电话代表:

return (from zipCode in _table
        where values.Contains(matchPhrase(zipCode))
        select zipCode).Distinct();

请注意,这可能会非常昂贵。您可能想先创建一个集合:

HashSet<T> valueSet = new HashSet<T>(values);
return _table.Where(x => valueSet.Contains(matchPhrase(x))
             .Distinct();

(我在这里删除了查询表达式,因为它在可读性方面弊大于利。)

【讨论】:

  • @bflemi3 我正要建议您不要将函数的参数命名为与方法本身相同的名称……这可能会引起混淆。
  • @JonSkeet 为什么使用 HashSet 然后使用 WhereContains 而 LINQ 连接方法完美地完成了这项任务以更高效的方式?
  • @CédricBignon:是什么让您认为联接性能更高?这基本上就是 join 将在内部执行的操作。加入是一个很好的解决方案,但我认为它不会表现得更好。
  • @bflemi3:这不仅是因为您在原始版本的代码中没有使用正确的名称,而且还强制横向滚动——最好格式化代码以避免在可能的情况下滚动。当然,我现在已经更新了。
  • @JonSkeet 好点,我已经改变以摆脱水平滚动。我也更新了我的问题。我认为我不够清楚,我误解了我应该使用什么方法签名。
【解决方案2】:

您在Contains 中忘记了(zipCode)

public IEnumerable<ZipCode> match<T>(IEnumerable<T> values, Func<ZipCode, T> matchPhrase) {
    return (from zipCode in _table
            where values.Contains(matchPhrase(zipCode))  // <- Here (and you don't need to specify <T>, the compiler deduce it from the argument)
            select zipCode).Distinct();
}

您可以使用 Join 方法来获得更好的性能(O(n) 中的复杂性):

public IEnumerable<ZipCode> match<T>(IEnumerable<T> values, Func<ZipCode, T> matchPhrase)
{
    return (from zipCode in _table
            join value in values on matchPhrase(zipCode) equals value
            select zipCode).Distinct();
}

【讨论】:

  • match 一个函数,并且 contains 根本不接受一个函数。
  • @bflemi3 看看你的方法的加入“版本”。它将复杂度从 O(n^2) 降低到 O(n)。
【解决方案3】:

Contains 只接受一个字符串作为参数,而不是一个表达式。您将无法在此级别上对其进行参数化。

您可以将整个 where 部分作为参数传递:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ConsoleApplication
{
    class Program
    {
         static void Main(string[] args)
         {
             var values = new List<string>();
             values.Add("123");

             Console.WriteLine(
                 Match(zip => values.Contains(zip.Code)).Count()); // -> 1

             Console.WriteLine(
                 Match(zip => values.Contains(zip.OtherCode)).Count()); // -> 0

             Console.Read();
         }

         public static IEnumerable<ZipCode> Match(Expression<Func<ZipCode, bool>> predicate)
         {
             var table = new List<ZipCode> 
                      { new ZipCode { Code = "123" }, new ZipCode { OtherCode = "234" } }
                .AsQueryable();

             return (from zipCode in table.Where(predicate)
                    select zipCode).Distinct();
         }
     }
     public class ZipCode
     {
         public string Code { get; set; }

         public string OtherCode { get; set; }
     }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 1970-01-01
    相关资源
    最近更新 更多