3

虽然在少数情况下我会使用方法链编写一些东西(特别是如果它只有一两个方法,比如 foo.Where(..).ToArray()),但在许多情况下,我更喜欢 LINQ 查询理解语法而是(规范中的“查询表达式”),例如:

var query =
    from filePath in Directory.GetFiles(directoryPath)
    let fileName = Path.GetFileName(filePath)
    let baseFileName = fileName.Split(' ', '_').First()
    group filePath by baseFileName into fileGroup
    select new
    {
        BaseFileName = fileGroup.Key,
        Count = fileGroup.Count(),
    };

在其中一些相当大的块中,我需要获取生成的 IEnumerable 并将其急切加载到数据结构(数组、列表等)中。这通常意味着:

  1. 添加另一个局部变量,例如 var queryResult = query.ToArray(); 或者

  2. 用括号包装查询并在 ToArray (或 ToList 或其他)上标记。

var query = (
    from filePath in Directory.GetFiles(directoryPath)
    let fileName = Path.GetFileName(filePath)
    let baseFileName = fileName.Split(' ', '_').First()
    group filePath by baseFileName into fileGroup
    select new
    {
        BaseFileName = fileGroup.Key,
        Count = fileGroup.Count(),
    }
).ToArray();

我试图找出其他人正在使用的选项 1) 已经在使用或 2) 可以认为添加一些额外的“上下文关键字”是可行的——只是可以像现有方法一样转换为扩展方法的东西,好像 LINQ 关键字是“本机”可扩展的 :)

我意识到这很可能意味着某种预处理(不确定 C# 在这个领域中有什么)或将编译器更改为Nemerle之类的东西(我认为这是一个选项,但不太确定? )。我还不太了解 Roslyn 所做/将支持什么,所以如果有人知道它是否可以允许某人像这样“扩展”C#,请插话!

我可能会使用最多的那些(虽然我确信还有很多其他的,但只是为了理解这个想法/我希望的是什么):

ascount - 转换为 Count()

int zFileCount =
    from filePath in Directory.GetFiles(directoryPath)
    where filePath.StartsWith("z")
    select filePath ascount;

这将“转换”(无论路径是什么,只要最终结果是)为:

int zFileCount = (
    from filePath in Directory.GetFiles(directoryPath)
    where filePath.StartsWith("z")
    select filePath
).Count();

相似地:

  • asarray - 转换为 ToArray()
  • aslist - 转换为 ToList()

(您显然可以继续使用 First()、Single()、Any() 等,但要控制问题范围 :)

我只对不需要传递参数的扩展方法感兴趣。我不想尝试用(例如)ToDictionary 或 ToLookup 来做这种事情。:)

所以,总结一下:

  • 想要将“ascount”、“aslist”和“asarray”添加到 linq 查询表达式中
  • 不知道这是否已经解决
  • 不知道 Nemerle 是否是一个不错的选择
  • 不知道 Roslyn 的故事是否会支持这种用法
4

3 回答 3

13

不是对您的问题的回答,而是对您的设计的一些思考。我们强烈考虑在 C# 4 中添加这样的功能,但由于我们没有可用的时间和资源而将其取消。

正如您所注意到的,查询理解语法的问题在于,混合“流利”和“理解”语法很难看。你想知道你的客户在伦敦有多少不同的姓氏,你最终用括号写了这个丑陋的东西:

d = (from c in customers 
     where c.City == "London" 
     select c.LastName)
    .Distinct()
    .Count();

呸。

我们考虑在理解语法中添加一个新的上下文关键字。我们假设关键字是“with”。然后你可以说:

d = from c in customers 
    where c.City == "London" 
    select c.LastName
    with Distinct() 
    with Count();

查询理解重写器会将其重写为适当的流利语法。

I really like this feature but it did not make the cut for C# 4 or 5. It would be nice to get it into a hypothetical future version of the language.

As always, Eric's musing about hypothetical features of unannounced products that might never exist are for entertainment purposes only.

于 2012-01-08T15:58:23.423 回答
3

想法是您可以编写自己的查询提供程序,将版本包装在其中System.Linq,然后调用ToArraySelect方法。然后你就会有一个using YourNamespace;而不是using System.Linq

Roslyn 不允许您扩展 C# 的语法,但您可以编写一个SyntaxRewriter更改 C# 程序语义的代码作为重建步骤。

于 2012-01-07T23:23:25.270 回答
2

正如其他人所说,Roslyn 不是您可能认为的那样。它不能用于扩展 C#。

以下所有代码都应该被认为是更多的头脑风暴和更少的推荐。它以意想不到的方式改变了 LINQ 的行为方式,您应该在使用类似的东西之前认真思考。

解决此问题的一种方法是select像这样修改子句:

int count = from i in Enumerable.Range(0, 10)
            where i % 2 == 0
            select new Count();

实现可能如下所示:

public  class Count
{}

public static class LinqExtensions
{
    public static int Select<T>(
        this IEnumerable<T> source, Func<T, Count> selector)
    {
        return source.Count();
    }
}

如果你把任何不在Count的东西select,它会像往常一样表现。

为数组做类似的事情需要更多的工作,因为你需要select指定你想要一个数组和你想要在那里的项目的选择器,但它是可行的。或者你可以使用两个selects:一个选择项目,另一个说你想要一个数组。

另一种选择(类似于凯文的建议)是使用扩展方法AsCount(),您可以像这样使用:

int count = from i in Enumerable.Range(0, 10).AsCount()
            where i % 2 == 0
            select i;

你可以像这样实现它:

public static class LinqExtensions
{
    public static Countable<T> AsCount<T>(this IEnumerable<T> source)
    {
        return new Countable<T>(source);
    }
}

public class Countable<T>
{
    private readonly IEnumerable<T> m_source;

    public Countable(IEnumerable<T> source)
    {
        m_source = source;
    }

    public Countable<T> Where(Func<T, bool> predicate)
    {
        return new Countable<T>(m_source.Where(predicate));
    }

    public Countable<TResult> Select<TResult>(Func<T, TResult> selector)
    {
        return new Countable<TResult>(m_source.Select(selector));
    }

    // other LINQ methods

    public static implicit operator int(Countable<T> countable)
    {
        return countable.m_source.Count();
    }
}

我不确定我是否喜欢这种方式。尤其是隐式演员感觉不对,但我认为没有其他办法。

于 2012-01-08T03:26:11.557 回答