虽然在少数情况下我会使用方法链来编写一些东西(尤其是如果它只是一个或两个方法,如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加载到数据结构(数组、列表等)中。这通常意味着:
添加另一个局部变量,如varqueryResult=query。ToArray();或
使用parens包装查询并在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"one_answers"asarray"添加到linq查询表达式中
- 不知道这个问题是否已经解决
- 不知道Nemerle是不是一个好的选择
- 不知道Roslyn的故事是否支持这种用途
这不是你的问题的答案,而是对你的设计的一些思考。我们曾强烈考虑在C#4中添加这样一个功能,但由于没有可用的时间和资源,我们取消了它。
正如您所注意到的,查询理解语法的问题是,将"流利"one_answers"理解"语法混合在一起是很难看的。你想知道你的客户在伦敦有多少不同的姓氏,结果你用括号写下了这个丑陋的东西:
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();
并且查询理解重写器将其重写为适当的流畅语法。
我真的很喜欢这个功能,但它没有进入C#4或5。如果能把它变成该语言的一个假设的未来版本,那就太好了。
和往常一样,孙对可能永远不存在的未经宣布的产品的假设功能的思考只是为了娱乐。
我们的想法是,您可以编写自己的查询提供程序,将版本封装在System.Linq
中,然后在其Select
方法中调用ToArray
。那么你只需要一个using YourNamespace;
而不是using System.Linq
。
Roslyn不允许您扩展C#的语法,但您可以编写一个SyntaxRewriter
,作为重建步骤来更改C#程序的语义。
正如其他人所说,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();
}
}
如果在select
中放入任何不是Count
的内容,它将照常运行。
对数组执行类似操作将需要更多的工作,因为您需要select
来指定您想要的数组和您想要的项选择器,但这是可行的。或者您可以使用两个select
:一个选择项目,另一个表示您想要一个数组。
另一种选择(类似于Kevin的建议)是使用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();
}
}
我不确定我是否喜欢这种方式。尤其是含蓄的演员阵容让人感觉不对,但我认为没有其他办法。