Update: Самым быстрым оказался алгоритм V7
Продолжение предыдущего вопроса.
Есть несколько вариантов реализации функции для проверки переменной на значение по умолчанию. Из комментариев к предыдущему вопросу я понял лишь то, что мне еще очень много предстоит выучить в C#. Помогите пожалуйста найти самый производительный и надежный вариант реализации функции.
В качестве параметра функции может быть все что угодно, включая класс, структуру, типы на подобие int? и так далее.
Некоторые варианты реализации функции:
V1:
bool IsDefault
V3 (улучшенный)
bool isDefault
V5 смешать V1 и большое кол-во перегрузок под все типы:
public class DefaultChecker
{
bool IsDefault(byte value) => value == 0;
bool IsDefault(byte? value) => value == null;
bool IsDefault(sbyte value) => value == 0;
bool IsDefault(sbyte? value) => value == null;
bool IsDefault(int value) => value == 0;
bool IsDefault(int? value) => value == null;
bool IsDefault(uint value) => value == 0;
bool IsDefault(uint? value) => value == null;
bool IsDefault(short value) => value == 0;
bool IsDefault(short? value) => value == null;
bool IsDefault(ushort value) => value == 0;
bool IsDefault(ushort? value) => value == null;
bool IsDefault(long value) => value == 0;
bool IsDefault(long? value) => value == null;
bool IsDefault(ulong value) => value == 0;
bool IsDefault(ulong? value) => value == null;
bool IsDefault(float value) => value == 0.0F;
bool IsDefault(float? value) => value == null;
bool IsDefault(double value) => value == 0.0D;
bool IsDefault(double? value) => value == null;
bool IsDefault(char value) => value == '\0';
bool IsDefault(char? value) => value == null;
bool IsDefault(bool value) => !value;
bool IsDefault(bool? value) => value == null;
bool IsDefault(string value) => value == null;
bool IsDefault(decimal value) => value == 0.0M;
bool IsDefault(decimal? value) => value == null;
public bool IsDefault
V6:
class RequireStruct
static bool IsDefault
static bool IsDefault
static bool IsDefault
V7 смешать V6 и большое кол-во перегрузок под все типы:
bool IsDefault(byte value) => value == 0;
bool IsDefault(byte? value) => value == null;
bool IsDefault(sbyte value) => value == 0;
bool IsDefault(sbyte? value) => value == null;
bool IsDefault(int value) => value == 0;
bool IsDefault(int? value) => value == null;
bool IsDefault(uint value) => value == 0;
bool IsDefault(uint? value) => value == null;
bool IsDefault(short value) => value == 0;
bool IsDefault(short? value) => value == null;
bool IsDefault(ushort value) => value == 0;
bool IsDefault(ushort? value) => value == null;
bool IsDefault(long value) => value == 0;
bool IsDefault(long? value) => value == null;
bool IsDefault(ulong value) => value == 0;
bool IsDefault(ulong? value) => value == null;
bool IsDefault(float value) => value == 0.0F;
bool IsDefault(float? value) => value == null;
bool IsDefault(double value) => value == 0.0D;
bool IsDefault(double? value) => value == null;
bool IsDefault(char value) => value == '\0';
bool IsDefault(char? value) => value == null;
bool IsDefault(bool value) => !value;
bool IsDefault(bool? value) => value == null;
bool IsDefault(string value) => value == null;
bool IsDefault(decimal value) => value == 0.0M;
bool IsDefault(decimal? value) => value == null;
bool IsDefault
---Реализации, которые не проходят тест---
V2:
public object GetDefaultValue(Type target)
{
Expression
public bool IsDefault(object o)
{
if(o == null)
throw new ArgumentNullException(nameof(o));
return o.Equals(GetDefaultValue(o.GetType()));
}
V3:
bool isDefault
V4:
//isDefault((object)0) этот вариант даст false
bool isDefault
Ответ
Для начала, нужно составить юнит-тесты на желаемую функцию. Например, такие:
Debug.Assert(IsDefault(default(string)));
Debug.Assert(IsDefault((object)default(string)));
Debug.Assert(!IsDefault(string.Empty));
Debug.Assert(!IsDefault((object)string.Empty));
Debug.Assert(IsDefault(default(int)));
Debug.Assert(!IsDefault(1));
Debug.Assert(IsDefault(default(int?)));
Debug.Assert(!IsDefault((int?)0));
Debug.Assert(IsDefault((object)0));
Debug.Assert(!IsDefault((object)1));
Пробуем различные имплементации:
V1 проходит.
V2 бросает ArgumentNullException вместо того, чтобы вернуть false, на Debug.Assert(IsDefault(default(string)));
V3 не проходит Debug.Assert(!IsDefault((int?)0)); проходит.
V4 не проходит Debug.Assert(IsDefault((object)0))
V5 проходит.
V6 проходит.
V7 проходит.
Окей, на текущий момент тесты проходят V1, V3, V5, V6 и V7. Тестировалось при помощи BenchmarkDotNet, результаты ниже. Вот результаты:
BenchmarkDotNet=v0.10.9, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i7-6700 CPU 3.40GHz (Skylake), ProcessorCount=8
Frequency=3328123 Hz, Resolution=300.4697 ns, Timer=TSC
[Host] : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2102.0
DefaultJob : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2102.0
Method | Mean | Error | StdDev |
------- |-----------:|---------:|---------:|
TestV1 | 1,793.9 ns | 4.960 ns | 4.639 ns |
TestV3 | 1,260.1 ns | 6.523 ns | 5.782 ns |
TestV5 | 1,195.5 ns | 9.269 ns | 7.740 ns |
TestV6 | 481.8 ns | 2.526 ns | 2.363 ns |
TestV7 | 445.3 ns | 1.847 ns | 1.728 ns |
Обратите внимание, что код проверки пробегает в худшем случае за менее двух, а в лучшем — за половину микросекунды (то есть, одной миллионной части секунды). Поэтому искать выигрыш в любой из реализаций нет особенного смысла: все реализации пробегают очень быстро. Единственный случай, при котором имеет смысл задуматься об оптимизации — это если такая вот операция вызывается десятки тысяч раз. (Но в этом случае, возможно, имеет смысл пересмотреть дизайн программы.)
Если кому интересно, вот код сравнения (длинный и скучный):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace Т
{
public class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run
public class Benchmarks
{
bool IsDefaultV1
bool IsDefaultV3
public bool IsDefaultV5(byte value) => value == 0;
public bool IsDefaultV5(byte? value) => value == null;
public bool IsDefaultV5(sbyte value) => value == 0;
public bool IsDefaultV5(sbyte? value) => value == null;
public bool IsDefaultV5(int value) => value == 0;
public bool IsDefaultV5(int? value) => value == null;
public bool IsDefaultV5(uint value) => value == 0;
public bool IsDefaultV5(uint? value) => value == null;
public bool IsDefaultV5(short value) => value == 0;
public bool IsDefaultV5(short? value) => value == null;
public bool IsDefaultV5(ushort value) => value == 0;
public bool IsDefaultV5(ushort? value) => value == null;
public bool IsDefaultV5(long value) => value == 0;
public bool IsDefaultV5(long? value) => value == null;
public bool IsDefaultV5(ulong value) => value == 0;
public bool IsDefaultV5(ulong? value) => value == null;
public bool IsDefaultV5(float value) => value == 0.0F;
public bool IsDefaultV5(float? value) => value == null;
public bool IsDefaultV5(double value) => value == 0.0D;
public bool IsDefaultV5(double? value) => value == null;
public bool IsDefaultV5(char value) => value == '\0';
public bool IsDefaultV5(char? value) => value == null;
public bool IsDefaultV5(bool value) => !value;
public bool IsDefaultV5(bool? value) => value == null;
public bool IsDefaultV5(string value) => value == null;
public bool IsDefaultV5(decimal value) => value == 0.0M;
public bool IsDefaultV5(decimal? value) => value == null;
public bool IsDefaultV5
class RequireStruct
static bool IsDefaultV6
static bool IsDefaultV7(byte value) => value == 0;
static bool IsDefaultV7(byte? value) => value == null;
static bool IsDefaultV7(sbyte value) => value == 0;
static bool IsDefaultV7(sbyte? value) => value == null;
static bool IsDefaultV7(int value) => value == 0;
static bool IsDefaultV7(int? value) => value == null;
static bool IsDefaultV7(uint value) => value == 0;
static bool IsDefaultV7(uint? value) => value == null;
static bool IsDefaultV7(short value) => value == 0;
static bool IsDefaultV7(short? value) => value == null;
static bool IsDefaultV7(ushort value) => value == 0;
static bool IsDefaultV7(ushort? value) => value == null;
static bool IsDefaultV7(long value) => value == 0;
static bool IsDefaultV7(long? value) => value == null;
static bool IsDefaultV7(ulong value) => value == 0;
static bool IsDefaultV7(ulong? value) => value == null;
static bool IsDefaultV7(float value) => value == 0.0F;
static bool IsDefaultV7(float? value) => value == null;
static bool IsDefaultV7(double value) => value == 0.0D;
static bool IsDefaultV7(double? value) => value == null;
static bool IsDefaultV7(char value) => value == '\0';
static bool IsDefaultV7(char? value) => value == null;
static bool IsDefaultV7(bool value) => !value;
static bool IsDefaultV7(bool? value) => value == null;
static bool IsDefaultV7(string value) => value == null;
static bool IsDefaultV7(decimal value) => value == 0.0M;
static bool IsDefaultV7(decimal? value) => value == null;
static bool IsDefaultV7
struct Test
List
Комментариев нет:
Отправить комментарий