代码之家  ›  专栏  ›  技术社区  ›  Erik Forbes

为什么使用.aseNumerable()而不是强制转换为IEnumerable?

  •  61
  • Erik Forbes  · 技术社区  · 15 年前

    上的扩展方法之一 IEnumerable<T> .AsEnumerable() . 此方法将对其调用的可枚举对象转换为的实例 IEnumerable<t> . 但是,由于对象必须实现 IEnumerable<t> 为了应用于此扩展方法,将转换为 IEnumerable<t> 是一件简单的事情 IEnumerable<t> . 我的问题是为什么这种方法存在?

    例子:

    List<string> strings = new List<string>() { "test", "test2", "test3" };
    IEnumerable<string> stringsEnum1 = strings.AsEnumerable();
    IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings;
    

    在上面的例子中, stringsEnum1 stringsEnum2 是等效的。扩展方法的意义是什么?

    编辑 :作为推论,为什么 .AsQueryable() 铸造时的方法 IQueryable<T> 是等价的吗?

    7 回复  |  直到 7 年前
        1
  •  79
  •   jason    15 年前

    可读性是这里的主要问题。认为

    Table.AsEnumerable().Where(somePredicate)
    

    ((IEnumerable<TableObject>)Table).Where(somePredicate).
    

    或者设想希望在SQL Server上执行查询的一部分,而在内存中执行其余部分:

    Table.Where(somePredicate)
         .Select(someProjection)
         .AsEnumerable()
         .SomethingElse()
    

    对战

    ((IEnumerable<SomeProjectionType>)Table.Where(somePredicate)
                                           .Select(someProjection))
                                           .SomethingElse()
    

    现在,关于为什么这种方法是有用的,想想 Table 在Linq to SQL中 DataContext . AS 是一个 IQueryable 它实现 IEnumerable . 当您调用 Where 方法 并通过枚举结果,执行最终导致SQL语句在SQL服务器上执行的代码。什么 AsEnumerable 是说,否,我不想使用Linq to SQL提供程序来执行 在哪里? ,我想使用linq to对象实现 在哪里? .

    因此列举

    Table.Where(somePredicate)
    

    导致在SQL Server上执行查询,而枚举

    table.aseNumerable().where(somePredicate)
    

    将由表示的表 进入内存并执行 在哪里? 内存中的功能(而不是在SQL Server上!)

    这就是重点 可枚举的 :允许您隐藏 可枚举的 方法,而不是使用标准实现。

        2
  •  15
  •   Erik Forbes    15 年前

    除了可读性之外,我还考虑了一个与查询实现相关的原因:对通过另一个Linq提供程序返回的匿名类型使用Linq to对象。不能强制转换为匿名类型(或匿名类型集合),但可以使用 .AsEnumerable() 为你演出。

    例子:

    // Get an IQueryable of anonymous types.
    var query = from p in db.PeopleTable /* Assume Linq to SQL */
                select new { Name = p.Name, Age = p.Age };
    
    // Execute the query and pull the results into an IEnumerable of anonymous types
    var enum = query.AsEnumerable();
    
    // Use Linq to Objects methods to further refine.
    var refined = from p in enum
                  select new
                  {
                      Name = GetPrettyName(p.Name),
                      DOB = CalculateDOB(p.Age, DateTime.Now)
                  };
    

    很明显,这里的原因是我们希望使用像Linq to SQL这样的东西将一些记录拉入匿名类型,然后使用客户端的Linq to对象执行一些自定义逻辑(通过Linq to SQL是不可能的)。

    铸造到 IEnumerable<_anon> 不可能,所以 AsMeaveRable() 是唯一的出路。

    感谢所有回答我的人帮助我拼凑起来。=)

        3
  •  4
  •   Timeless    8 年前

    当我读这本书的时候 C# 6.0 in a Nutshell . 下面是一个例子 AsEnumerable 在书中。


    目的是为了 IQueryable<T> 顺序到 IEnumerable<T> ,强制后续查询运算符绑定到可枚举运算符,而不是可查询运算符。这将导致执行查询的其余部分 局部地 .

    举例来说,假设我们 MedicalArticles 在SQL Server中的表,希望使用Linq to SQL或EF检索有关流感的所有文章,这些文章的摘要包含的单词少于100个。对于后一个谓词,我们需要一个正则表达式:

    Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");
    
    var query = dataContext.MedicalArticles
                .Where (article => article.Topic == "influenza" &&
                wordCounter.Matches (article.Abstract).Count < 100);
    

    问题是SQL Server不支持正则表达式,因此Linq to DB提供程序将抛出异常,抱怨查询无法转换为SQL。我们可以通过以下两个步骤来解决这个问题:首先通过Linq to SQL查询检索所有关于流感的文章,然后在本地筛选不到100个单词的摘要:

    Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");
    
    IEnumerable<MedicalArticle> sqlQuery = dataContext.MedicalArticles
        .Where (article => article.Topic == "influenza");
    
    IEnumerable<MedicalArticle> localQuery = sqlQuery
        .Where (article => wordCounter.Matches (article.Abstract).Count < 100);
    

    使用AsEnumerable,我们可以在单个查询中执行相同的操作:

    var query = dataContext.MedicalArticles
          .Where (article => article.Topic == "influenza")
          .AsEnumerable()
          .Where (article => wordCounter.Matches (article.Abstract).Count < 100);
    

    调用AsEnumeratable的另一种方法是调用ToArray或ToList。 AsEnumeratable的优点是它不强制立即执行查询 也不会创建任何存储结构。

        4
  •  3
  •   Meta-Knight    15 年前

    这只是一个向IEnumerable投射的最好和最短的方法。如果您在Reflector中查看它,您可以看到它除了将对象作为IEnumerable返回之外什么都不做。

    来自MSDN:

    不可数的 t源)(IEnumerable(of tsource)) 方法只有对 更改源的编译时类型 从实现的类型 从t的ienumerable到t的ienumerable 本身。

        5
  •  3
  •   james    14 年前

    匿名类型是提供这些扩展方法的主要原因。 (不能在泛型参数中使用匿名类型) 但是方法调用可以使用类型推断,允许您忽略在泛型参数中指定类型。

        6
  •  3
  •   Richard Petheram    7 年前

    如果对象上有与Linq扩展方法同名的方法,则隐藏扩展方法。使用AsEnumerable允许您访问扩展名。

    这在SP1中似乎是新的。

    昨天我有一行代码从数据表中提取成员标识符:

    var lMmIds = new List<int>(
        lDmMember.DataTable.Select(R => R.MmId)
    );
    

    在我安装了SP1之前,它工作得很好。现在,除非它读到

    var lMmIds = new List<int>(
        lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId)
    );
    

    编辑:我找到了真正的原因

    这样,您就可以在同一个LINQ语句中同时使用远程方法(例如SQL语句中的where)和本地方法。如果不使用AsEnumerable(即仅强制转换),它将使查询生成器尝试为包含本地方法的远程执行创建表达式树。将asenumerable放入查询将导致该查询的其余部分在本地对远程查询的结果执行。

    https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

    表示数据库表的表类型可以具有一个将谓词参数作为表达式树并将该树转换为用于远程执行的SQL的where方法。如果不需要远程执行,例如,因为谓词调用本地方法,则可以使用AsEnumerable方法隐藏自定义方法,而使标准查询运算符可用。

        7
  •  2
  •   LukeH    15 年前

    如您所说,如果类型已经实现 IEnumerable<T> 那么,在转换到接口或调用 AsEnumerable 方法。

    我猜,这只是个猜测,是不是 可枚举的 提高可读性并保留其他LINQ扩展方法的流畅签名:

    var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty);
    
    // vs
    
    var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);
    

    它还允许不实现的类型 IEnumerable<t> - for example, DataTable -有自己版本的 可枚举的 延伸。这允许您继续使用 同样的模式 在针对这些类型的查询中-即使它是不同的 可枚举的 您正在调用的方法-无需担心该类型是否真正实现 IEnumerable<t> .