[英]Is there an elegant (or more performant) way to (not) iterate through C# enums?
[英]What is the best and performant way to compare enums in c#?
我正在努力比较枚举。 我已经测试了 5 种不同的方式(等于、HasFlag、== 运算符、按位运算符和 is 运算符)。 其中两个(HasFlag 和 Equal)不是用于比较两个枚举的好方法。 但是其他三种方式让我感到困惑。 我试图对它们进行基准测试,但它们给了我各种结果。 这里的测试方法。
[Flags]
public enum Typess
{
First = 2,
Second = 4,
public int testCount = 1000;
public int sampleCount = 10000000;
GC.Collect();
var stopwatch = new Stopwatch();
float avarageTime1 = 0f;
for (int i = 0; i < testCount; i++)
{
stopwatch.Reset();
stopwatch.Start();
for (int j = 0; j < sampleCount; j++)
{
TestEnum(Typess.First);
}
stopwatch.Stop();
avarageTime1 += stopwatch.ElapsedMilliseconds;
}
Debug.Log(" Enum Test Consumed time : " + avarageTime1 / (float)testCount + " ms");
GC.Collect();
float avarageTime2 = 0f;
for (int i = 0; i < testCount; i++)
{
stopwatch.Reset();
stopwatch.Start();
for (int j = 0; j < sampleCount; j++)
{
TestEnumByte(Typess.First);
}
stopwatch.Stop();
avarageTime2 += stopwatch.ElapsedMilliseconds;
}
Debug.Log("Enum Byte Test Consumed time : " + avarageTime2 / (float)testCount + " ms");
GC.Collect();
float avarageTime3 = 0f;
for (int i = 0; i < testCount; i++)
{
stopwatch.Reset();
stopwatch.Start();
for (int j = 0; j < sampleCount; j++)
{
TestEnumIs(Typess.First);
}
stopwatch.Stop();
avarageTime3 += stopwatch.ElapsedMilliseconds;
}
Debug.Log("Enum Is Test Consumed time : " + avarageTime3 / (float)testCount + " ms");
private bool TestEnum(Typess testType) => testType == Typess.Second;
private bool TestEnumByte(Typess testType) => (testType & Typess.Second) != 0;
private bool TestEnumIs(Typess testType) => testType is Typess.Second;
为什么这三种方法给我不同的结果? 难道我做错了什么? 谢谢
让我们改进您的测试工具:
IEnumerable<TimeSpan> GenerateTestRuns(Func<Typess, bool> f, int samples)
{
while (true)
{
int generation = GC.GetGeneration(0);
Stopwatch stopwatch = Stopwatch.StartNew();
for (int j = 0; j < samples; j++)
{
f(Typess.First);
}
stopwatch.Stop();
if (generation == GC.GetGeneration(0))
{
yield return stopwatch.Elapsed;
}
}
}
这段代码的美妙之处在于它会产生零次或多次运行的测试,其中没有垃圾 collections 。 缺点是如果一直有垃圾collections这段代码会陷入死循环。
在这种情况下,它运行良好并且始终产生价值。
这是我运行的测试:
Dictionary<string, Func<Typess, bool>> fs = new Dictionary<string, Func<Typess, bool>>()
{
{ "testType == Typess.Second", testType => testType == Typess.Second },
{ "(testType & Typess.Second) != 0", testType => (testType & Typess.Second) != 0 },
{ "testType is Typess.Second", testType => testType is Typess.Second },
};
现在实际运行测试的代码是这样的:
int testCount = 1000;
int sampleCount = 10000000;
Dictionary<string, TimeSpan> results = fs.ToDictionary(x => x.Key, x => TimeSpan.Zero);
for (int i = 0; i < testCount; i++)
{
foreach (KeyValuePair<string, Func<Typess, bool>> kvp in fs)
{
foreach (TimeSpan ts in GenerateTestRuns(kvp.Value, sampleCount).Take(1))
{
results[kvp.Key] += ts;
}
}
}
foreach (KeyValuePair<string, TimeSpan> kvp in results)
{
Console.WriteLine($"{kvp.Key} each run in {kvp.Value.TotalMilliseconds / testCount} ms");
}
我使用优化代码运行,并检查 IL 是否正在调用函数。
我得到的结果相当一致:
(1)
testType == Typess.Second each run in 21.3049772 ms
(testType & Typess.Second) != 0 each run in 21.321223999999997 ms
testType is Typess.Second each run in 26.525582 ms
(2)
testType == Typess.Second each run in 20.9722699 ms
(testType & Typess.Second) != 0 each run in 20.9309871 ms
testType is Typess.Second each run in 26.0593333 ms
==
运算符:此运算符比较两个枚举变量的值,如果相等则返回true
,否则返回false
。 这是比较两个枚举值的推荐方法,因为它快速且高效。
按位运算符:您可以使用按位运算符&
来比较用[Flags]
属性修饰的枚举值。 此运算符检查第一个枚举值是否包含第二个枚举值中的所有标志。
is 运算符: is 运算符检查 object 是否属于特定类型。 它可用于检查枚举值是否属于特定枚举类型,但不建议将其用于比较两个枚举值,因为它比其他选项更慢且效率更低。 执行额外的类型检查。
通常, ==
运算符是比较枚举值的最有效方法,其次是[Flags]
枚举值的按位 & 运算符。 由于性能较低,通常应避免使用 Equal 方法和is
运算符来比较枚举值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.