回答以下问题: How to convert MatchCollection to string array
鉴于两个Linq表达式:
var arr = Regex.Matches(strText, @"\b[A-Za-z-']+\b")
.OfType<Match>() //OfType
.Select(m => m.Groups[0].Value)
.ToArray();
和
var arr = Regex.Matches(strText, @"\b[A-Za-z-']+\b")
.Cast<Match>() //Cast
.Select(m => m.Groups[0].Value)
.ToArray();
OfType&LT;&GT;用户Alex进行基准测试的速度略快(并由我自己确认)。
这对我来说似乎违反直觉,因为我已经想过OfType&lt;&gt;必须做'是'比较,和一个演员(T)。
任何启示都会被理解为什么会这样:)
答案 0 :(得分:13)
我的基准测试与您的基准测试不一致。
我为亚历克斯运行了相同的基准测试并获得了相反的结果。然后,我稍微调整了基准,并再次观察到Cast
比OfType
更快。
其中没有多少,但我相信 Cast
确实有优势,因为它的迭代器更简单。 (没有is
检查。)
编辑:实际上经过一些进一步的调整后,我设法让 Cast
比OfType
快50倍。
以下是我发现迄今为止发现的最大差异的基准代码:
Stopwatch sw1 = new Stopwatch();
Stopwatch sw2 = new Stopwatch();
var ma = Enumerable.Range(1, 100000).Select(i => i.ToString()).ToArray();
var x = ma.OfType<string>().ToArray();
var y = ma.Cast<string>().ToArray();
for (int i = 0; i < 1000; i++)
{
if (i%2 == 0)
{
sw1.Start();
var arr = ma.OfType<string>().ToArray();
sw1.Stop();
sw2.Start();
var arr2 = ma.Cast<string>().ToArray();
sw2.Stop();
}
else
{
sw2.Start();
var arr2 = ma.Cast<string>().ToArray();
sw2.Stop();
sw1.Start();
var arr = ma.OfType<string>().ToArray();
sw1.Stop();
}
}
Console.WriteLine("OfType: " + sw1.ElapsedMilliseconds.ToString());
Console.WriteLine("Cast: " + sw2.ElapsedMilliseconds.ToString());
Console.ReadLine();
我做过的调整:
在我的机器上,这导致Cast
约为350毫秒,OfType
约为18000毫秒。
我认为最大的区别在于我们不再计算 MatchCollection
找到下一场比赛的时间。 (或者,在我的代码中,int.ToString()
需要多长时间。)这大大降低了信噪比。
编辑:正如六个变量所指出的那样,造成这种巨大差异的原因是Cast
会短路,如果它可以投射整个IEnumerable
,则不会打扰单个项目。当我从使用Regex.Matches
切换到数组以避免测量正则表达式处理时间时,我也切换到使用可投射到IEnumerable<string>
的东西,从而激活了这个短路。当我更改基准以禁用此短路时,我获得 轻微 优势Cast
而不是 大量 一个。
答案 1 :(得分:9)
OfType()
应该更慢,因为在实际的显式强制转换操作之前进行安全类型is
检查,同时Cast()
只执行显式强制转换。
理论上OfType
在许多元素具有“错误类型”的情况下会更快,所以循环在is
检查后进一步枚举,如果Cast()
在同一个集合上,你会在“错误类型”的每个元素上以InvalidCastException
结束,因此这将相对较慢。
使用ILSpy提取的源代码:
// System.Linq.Enumerable
private static IEnumerable<TResult> OfType<TResult>(IEnumerable source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
foreach (object current in source)
{
// **Type check**
if (current is TResult)
{
// **Explicit cast**
yield return (TResult)current;
}
}
yield break;
}
// System.Linq.Enumerable
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
IEnumerable<TResult> enumerable = source as IEnumerable<TResult>;
if (enumerable != null)
{
return enumerable;
}
if (source == null)
{
throw Error.ArgumentNull("source");
}
foreach (object current in source)
{
// **Explicit cast only**
yield return (TResult)current;
}
yield break;
}
答案 2 :(得分:6)
只需颠倒方法中OfType
和Cast
的顺序,您就会注意到没有区别。第一个总是比第二个运行得快。 这是一个糟糕的微基准测试的例子。
将代码包装在一个循环中,以随机顺序运行它们:
OfType: 1224
Cast: 2815
Cast: 2961
OfType: 3010
OfType: 3027
Cast: 2987
...
然后再说:
Cast: 1207
OfType: 2781
Cast: 2930
OfType: 2964
OfType: 2964
OfType: 2987
...
解除导致问题的Regex.Matches
:
Cast: 1247
OfType: 210
OfType: 170
Cast: 171
...
和
OfType: 1225
Cast: 202
OfType: 171
Cast: 192
Cast: 415
所以,不。 OfType
并不比Cast
快。不,Cast
并不比OfType
快。
答案 3 :(得分:1)
实际上isof()首先检查类型,然后对其进行强制转换,其中cast()只执行第二部分。所以很明显isof()会比直接投射慢