Иногда рефлексивные вызовы дороги в терминах производительности и не могут быть опущены.
В этой статье представлены паттерны, позволяющие существенно повысить производительность множественных рефлексивных вызовов посредством техники виртуализации (кэширования) результатов.

Рассмотрим следующий метод:
Важный паттерн в подобных случаях — это кэширование локальной переменной в качестве статической, например:
Но как поступить в случае обобщённого (generic) метода?
Существует практичное обобщённое решение, взгляните.
TypeOf.cs
*Примечательно, что до момента добавления обобщённых классов и методов, C# уже имел поддержку ряда обобщённых операторов: `typeof`, `is`, `as`
Что насчёт другого сценария?
Можем попробовать.
RipeType.cs
TypeOf.cs
* Примечание: как указали в комментариях, для более надёжной потокобезопасности нужно использовать ConcurrentDictionary (это может повлиять на результаты тестов)
Lock.cs
Итак, теперь можно использовать:
Что насчёт недостатков `TypeOf` паттерна?
* `typeof(List<>)` допустимо
* `TypeOf<List<>>` не допустимо
Как решить?
где
Самое время для бенчмарков!
Заключение
`TypeOf` и `RipeType` паттерны позволяют ощутимо улучшить производительность множественных рекурсивных вызовов в некоторых сценариях на различных CLR.
В этой статье представлены паттерны, позволяющие существенно повысить производительность множественных рефлексивных вызовов посредством техники виртуализации (кэширования) результатов.

Рассмотрим следующий метод:
static void AnyPerformanceCriticalMethod() { var anyTypeName = typeof(AnyType).Name; /* ... using of anyTypeName ... */ }
Важный паттерн в подобных случаях — это кэширование локальной переменной в качестве статической, например:
/* much better! */ static readonly string AnyTypeName = typeof(AnyType).Name; static void AnyPerformanceCriticalMethod() { /* ... using of AnyTypeName ... */ }
Но как поступить в случае обобщённого (generic) метода?
static void AnyPerformanceCriticalMethod<T>() { var anyTypeName = typeof(T).Name; /* ... using of anyTypeName ... */ }
Существует практичное обобщённое решение, взгляните.
TypeOf.cs
public static class TypeOf<T> { /* Important! Should be a readonly variable for best performance */ public static readonly Type Raw = typeof(T); public static readonly string Name = Raw.Name; public static readonly Assembly Assembly = Raw.Assembly; public static readonly bool IsValueType = Raw.IsValueType; /* etc. */ } static void AnyPerformanceCriticalMethod<T>() { var anyTypeName = TypeOf<T>.Name; /* ... using of anyTypeName ... */ }
*Примечательно, что до момента добавления обобщённых классов и методов, C# уже имел поддержку ряда обобщённых операторов: `typeof`, `is`, `as`
Что насчёт другого сценария?
static void AnyPerformanceCriticalMethod(object item) { var itemTypeName = o.GetType().Name; /* ... using of anyTypeName ... */ }
Можем попробовать.
RipeType.cs
public class RipeType { internal RipeType(Type raw) { Raw = raw; Name = raw.Name; Assembly = raw.Assembly; IsValueType = raw.IsValueType; /* etc. */ } public static Type Raw { get; } public string Name { get; } public Assembly Assembly { get; } public bool IsValueType { get; } /* etc. */ }
TypeOf.cs
* Примечание: как указали в комментариях, для более надёжной потокобезопасности нужно использовать ConcurrentDictionary (это может повлиять на результаты тестов)
public static class TypeOf { private static readonly object SyncRoot = new object(); private static readonly Dictionary<Type, RipeType> RawToRipe = new Dictionary<Type, RipeType>(); public static RipeType ToRipeType(this Type type) => RawToRipe.TryGetValue(type, out var typeData) ? typeData : Lock.Invoke(SyncRoot, () => RawToRipe.TryGetValue(type, out typeData) ? typeData // may catch item created into a different thread : RawToRipe[type] = new RipeType(type)); public static RipeType GetRipeType(this object o) => o.GetType().ToRipeType(); }
Lock.cs
public static class Lock { public static void Invoke<TSyncContext>(TSyncContext customSyncContext, Action action) { lock (customSyncContext) action(); } public static TResult Invoke<TSyncContext, TResult>(TSyncContext customSyncContext, Func<TResult> func) { lock (customSyncContext) return func(); } }
Итак, теперь можно использовать:
static void AnyPerformanceCriticalMethod(object item) { var itemTypeName = o.GetRipeType().Name; /* ... using of anyTypeName ... */ }
Что насчёт недостатков `TypeOf` паттерна?
* `typeof(List<>)` допустимо
* `TypeOf<List<>>` не допустимо
Как решить?
var listAssemby = TypeOf.List.Assembly;
где
public static class TypeOf { /* ... */ public static readonly RipeType Object = typeof(object).ToRipeType(); public static readonly RipeType String = typeof(string).ToRipeType(); public static readonly RipeType Array = typeof(Array).ToRipeType(); public static readonly RipeType Type = typeof(Type).ToRipeType(); public static readonly RipeType List = typeof(List<>).ToRipeType(); public static readonly RipeType IList = typeof(IList<>).ToRipeType(); public static readonly RipeType Dictionary = typeof(Dictionary<,>).ToRipeType(); public static readonly RipeType IDictionary = typeof(IDictionary<,>).ToRipeType(); public static readonly RipeType KeyValuePair = typeof(KeyValuePair<,>).ToRipeType(); public static readonly RipeType DictionaryEntry = typeof(DictionaryEntry).ToRipeType(); }
Самое время для бенчмарков!
typeof vs. TypeOf [BenchmarkDotNet - code]
[ CoreJob, ClrJob, MonoJob("Mono", @"C:\Program Files\Mono\bin\mono.exe") ] public class TypeOfBenchmarks { [Benchmark] public Type typeof_int() => typeof(int); [Benchmark] public Type TypeOf_int() => TypeOf<int>.Raw; [Benchmark] public Type typeof_string() => typeof(string); [Benchmark] public Type TypeOf_string() => TypeOf<string>.Raw; [Benchmark] public string typeof_int_Name() => typeof(int).Name; [Benchmark] public string TypeOf_int_Name() => TypeOf<int>.Name; [Benchmark] public string typeof_string_Name() => typeof(string).Name; [Benchmark] public string TypeOf_string_Name() => TypeOf<string>.Name; [Benchmark] public Assembly typeof_int_Assembly() => typeof(int).Assembly; [Benchmark] public Assembly TypeOf_int_Assembly() => TypeOf<int>.Assembly; [Benchmark] public Assembly typeof_string_Assembly() => typeof(string).Assembly; [Benchmark] public Assembly TypeOf_string_Assembly() => TypeOf<string>.Assembly; [Benchmark] public bool typeof_int_IsValueType() => typeof(int).IsValueType; [Benchmark] public bool TypeOf_int_IsValueType() => TypeOf<int>.IsValueType; [Benchmark] public bool typeof_string_IsValueType() => typeof(string).IsValueType; [Benchmark] public bool TypeOf_string_IsValueType() => TypeOf<string>.IsValueType; }
typeof vs. TypeOf [BenchmarkDotNet - results]
Total time: 00:23:34 (1414.47 sec) // * Summary * BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 Intel Core i7-3517U CPU 1.90GHz (Ivy Bridge), 1 CPU, 4 logical and 2 physical cores Frequency=2338440 Hz, Resolution=427.6355 ns, Timer=TSC .NET Core SDK=2.1.302 [Host] : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT Clr : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3160.0 Core : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT Mono : Mono 5.12.0 (Visual Studio), 64bit Method | Job | Runtime | Mean | Error | StdDev | -------------------------- |----- |-------- |------------:|-----------:|-----------:| typeof_int | Clr | Clr | 3.2686 ns | 0.0490 ns | 0.0434 ns | TypeOf_int | Clr | Clr | 0.0495 ns | 0.1124 ns | 0.0939 ns | typeof_string | Clr | Clr | 3.1980 ns | 0.0288 ns | 0.0270 ns | TypeOf_string | Clr | Clr | 0.0520 ns | 0.0773 ns | 0.0723 ns | typeof_int_Name | Clr | Clr | 19.4201 ns | 0.1220 ns | 0.1141 ns | TypeOf_int_Name | Clr | Clr | 0.0082 ns | 0.0169 ns | 0.0159 ns | typeof_string_Name | Clr | Clr | 19.5041 ns | 0.1397 ns | 0.1090 ns | TypeOf_string_Name | Clr | Clr | 0.0007 ns | 0.0031 ns | 0.0028 ns | typeof_int_Assembly | Clr | Clr | 33.8565 ns | 0.6931 ns | 0.5788 ns | TypeOf_int_Assembly | Clr | Clr | 0.0034 ns | 0.0130 ns | 0.0115 ns | typeof_string_Assembly | Clr | Clr | 33.9922 ns | 0.2244 ns | 0.1989 ns | TypeOf_string_Assembly | Clr | Clr | 0.0001 ns | 0.0004 ns | 0.0003 ns | typeof_int_IsValueType | Clr | Clr | 56.1685 ns | 0.3858 ns | 0.3420 ns | TypeOf_int_IsValueType | Clr | Clr | 0.4990 ns | 0.0141 ns | 0.0132 ns | typeof_string_IsValueType | Clr | Clr | 94.0358 ns | 0.4386 ns | 0.3662 ns | TypeOf_string_IsValueType | Clr | Clr | 0.4960 ns | 0.0109 ns | 0.0102 ns | typeof_int | Core | Core | 1.9114 ns | 0.0527 ns | 0.0493 ns | TypeOf_int | Core | Core | 6.1310 ns | 0.0494 ns | 0.0462 ns | typeof_string | Core | Core | 2.2120 ns | 0.0522 ns | 0.0436 ns | TypeOf_string | Core | Core | 6.1174 ns | 0.0481 ns | 0.0401 ns | typeof_int_Name | Core | Core | 19.5100 ns | 0.1998 ns | 0.1771 ns | TypeOf_int_Name | Core | Core | 6.1495 ns | 0.0829 ns | 0.0735 ns | typeof_string_Name | Core | Core | 19.3662 ns | 0.0895 ns | 0.0793 ns | TypeOf_string_Name | Core | Core | 6.1589 ns | 0.0314 ns | 0.0278 ns | typeof_int_Assembly | Core | Core | 23.4876 ns | 0.1885 ns | 0.1763 ns | TypeOf_int_Assembly | Core | Core | 6.1362 ns | 0.0415 ns | 0.0388 ns | typeof_string_Assembly | Core | Core | 25.5613 ns | 0.2293 ns | 0.2033 ns | TypeOf_string_Assembly | Core | Core | 6.1082 ns | 0.0352 ns | 0.0312 ns | typeof_int_IsValueType | Core | Core | 49.8048 ns | 0.2305 ns | 0.1925 ns | TypeOf_int_IsValueType | Core | Core | 7.1171 ns | 0.0477 ns | 0.0423 ns | typeof_string_IsValueType | Core | Core | 84.8155 ns | 0.7962 ns | 0.7058 ns | TypeOf_string_IsValueType | Core | Core | 7.0987 ns | 0.0521 ns | 0.0487 ns | typeof_int | Mono | Mono | 0.0725 ns | 0.0229 ns | 0.0214 ns | TypeOf_int | Mono | Mono | 3.0123 ns | 0.0652 ns | 0.0610 ns | typeof_string | Mono | Mono | 0.0185 ns | 0.0206 ns | 0.0193 ns | TypeOf_string | Mono | Mono | 9.3828 ns | 0.0863 ns | 0.0765 ns | typeof_int_Name | Mono | Mono | 429.8195 ns | 4.4049 ns | 3.6783 ns | TypeOf_int_Name | Mono | Mono | 2.3856 ns | 0.1608 ns | 0.1426 ns | typeof_string_Name | Mono | Mono | 439.3774 ns | 1.2985 ns | 1.2146 ns | TypeOf_string_Name | Mono | Mono | 8.8580 ns | 0.0728 ns | 0.0646 ns | typeof_int_Assembly | Mono | Mono | 223.5933 ns | 0.6152 ns | 0.5454 ns | TypeOf_int_Assembly | Mono | Mono | 2.2587 ns | 0.0494 ns | 0.0462 ns | typeof_string_Assembly | Mono | Mono | 227.3259 ns | 0.6448 ns | 0.5716 ns | TypeOf_string_Assembly | Mono | Mono | 9.3276 ns | 0.1215 ns | 0.1136 ns | typeof_int_IsValueType | Mono | Mono | 490.2376 ns | 4.3860 ns | 4.1027 ns | TypeOf_int_IsValueType | Mono | Mono | 3.1849 ns | 0.0145 ns | 0.0129 ns | typeof_string_IsValueType | Mono | Mono | 997.4254 ns | 11.6159 ns | 10.8655 ns | TypeOf_string_IsValueType | Mono | Mono | 9.6504 ns | 0.0354 ns | 0.0331 ns |
Type vs. RipeType [BenchmarkDotNet - code]
[ CoreJob, ClrJob, MonoJob("Mono", @"C:\Program Files\Mono\bin\mono.exe") ] public class RipeTypeBenchmarks { static object o = new object(); readonly Type rawType = o.GetType(); readonly RipeType ripeType = o.GetRipeType(); [Benchmark] public string RawType_Name() => rawType.Name; [Benchmark] public string RipeType_Name() => ripeType.Name; [Benchmark] public string GetRawType_Name() => o.GetType().Name; [Benchmark] public string GetRipeType_Name() => o.GetRipeType().Name; [Benchmark] public Assembly RawType_Assembly() => rawType.Assembly; [Benchmark] public Assembly RipeType_Assembly() => ripeType.Assembly; [Benchmark] public Assembly GetRawType_Assembly() => o.GetType().Assembly; [Benchmark] public Assembly GetRipeType_Assembly() => o.GetRipeType().Assembly; [Benchmark] public bool RawType_IsValueType() => rawType.IsValueType; [Benchmark] public bool RipeType_IsValueType() => ripeType.IsValueType; [Benchmark] public bool GetRawType_IsValueType() => o.GetType().IsValueType; [Benchmark] public bool GetRipeType_IsValueType() => o.GetRipeType().IsValueType; }
Type vs. RipeType [BenchmarkDotNet - results]
Total time: 00:14:59 (899.57 sec) // * Summary * BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 Intel Core i7-3517U CPU 1.90GHz (Ivy Bridge), 1 CPU, 4 logical and 2 physical cores Frequency=2338440 Hz, Resolution=427.6355 ns, Timer=TSC .NET Core SDK=2.1.302 [Host] : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT Clr : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3160.0 Core : .NET Core 2.1.2 (CoreCLR 4.6.26628.05, CoreFX 4.6.26629.01), 64bit RyuJIT Mono : Mono 5.12.0 (Visual Studio), 64bit Method | Job | Runtime | Mean | Error | StdDev | ------------------------ |----- |-------- |------------:|----------:|----------:| RawType_Name | Clr | Clr | 10.2733 ns | 0.1112 ns | 0.1040 ns | RipeType_Name | Clr | Clr | 0.0164 ns | 0.0220 ns | 0.0206 ns | GetRawType_Name | Clr | Clr | 15.3661 ns | 0.4064 ns | 0.7431 ns | GetRipeType_Name | Clr | Clr | 43.3530 ns | 0.4160 ns | 0.3474 ns | RawType_Assembly | Clr | Clr | 19.8898 ns | 0.1967 ns | 0.1840 ns | RipeType_Assembly | Clr | Clr | 0.0002 ns | 0.0010 ns | 0.0009 ns | GetRawType_Assembly | Clr | Clr | 22.7084 ns | 0.1512 ns | 0.1340 ns | GetRipeType_Assembly | Clr | Clr | 43.1685 ns | 0.3532 ns | 0.3304 ns | RawType_IsValueType | Clr | Clr | 35.7668 ns | 0.2840 ns | 0.2517 ns | RipeType_IsValueType | Clr | Clr | 0.0005 ns | 0.0020 ns | 0.0018 ns | GetRawType_IsValueType | Clr | Clr | 39.6176 ns | 0.2465 ns | 0.2306 ns | GetRipeType_IsValueType | Clr | Clr | 43.4645 ns | 0.9240 ns | 0.8643 ns | RawType_Name | Core | Core | 10.7102 ns | 0.1705 ns | 0.1511 ns | RipeType_Name | Core | Core | 0.0075 ns | 0.0154 ns | 0.0144 ns | GetRawType_Name | Core | Core | 12.8294 ns | 0.0698 ns | 0.0653 ns | GetRipeType_Name | Core | Core | 38.7723 ns | 0.2665 ns | 0.2493 ns | RawType_Assembly | Core | Core | 13.1644 ns | 0.0729 ns | 0.0682 ns | RipeType_Assembly | Core | Core | 0.0174 ns | 0.0207 ns | 0.0194 ns | GetRawType_Assembly | Core | Core | 15.3733 ns | 0.1252 ns | 0.1110 ns | GetRipeType_Assembly | Core | Core | 38.7863 ns | 0.3133 ns | 0.2616 ns | RawType_IsValueType | Core | Core | 32.9788 ns | 0.4456 ns | 0.3721 ns | RipeType_IsValueType | Core | Core | 0.0365 ns | 0.0128 ns | 0.0107 ns | GetRawType_IsValueType | Core | Core | 35.4362 ns | 0.2927 ns | 0.2595 ns | GetRipeType_IsValueType | Core | Core | 39.8377 ns | 0.2895 ns | 0.2708 ns | RawType_Name | Mono | Mono | 287.4362 ns | 2.3812 ns | 2.2274 ns | RipeType_Name | Mono | Mono | 0.4614 ns | 0.0320 ns | 0.0299 ns | GetRawType_Name | Mono | Mono | 288.2094 ns | 2.2540 ns | 2.1084 ns | GetRipeType_Name | Mono | Mono | 54.3390 ns | 0.2807 ns | 0.2625 ns | RawType_Assembly | Mono | Mono | 143.6474 ns | 0.7524 ns | 0.7038 ns | RipeType_Assembly | Mono | Mono | 0.7015 ns | 0.0261 ns | 0.0244 ns | GetRawType_Assembly | Mono | Mono | 144.0314 ns | 3.2279 ns | 3.0194 ns | GetRipeType_Assembly | Mono | Mono | 54.5511 ns | 0.2955 ns | 0.2619 ns | RawType_IsValueType | Mono | Mono | 277.4973 ns | 1.4938 ns | 1.3242 ns | RipeType_IsValueType | Mono | Mono | 0.5206 ns | 0.0176 ns | 0.0156 ns | GetRawType_IsValueType | Mono | Mono | 280.7464 ns | 2.1995 ns | 1.8367 ns | GetRipeType_IsValueType | Mono | Mono | 58.5908 ns | 0.1690 ns | 0.1498 ns |
Manual benchmarks - Code (much faster runs)
using System; using System.Diagnostics; using System.Linq; using Ace.Base.Benchmarking.Benchmarks; using BenchmarkDotNet.Running; namespace Ace.Base.Benchmarking { static class Program { private const long WarmRunsCount = 1000; private const long HotRunsCount = 10000000; // 10 000 000 static void Main() { //BenchmarkRunner.Run<TypeOfBenchmarks>(); //BenchmarkRunner.Run<RipeTypeBenchmarks>(); TypeofVsTypeOf(); RawTypeVsRipeType(); Console.ReadKey(); } static void RawTypeVsRipeType() { Console.WriteLine(); Console.WriteLine($"Count of warm iterations: {WarmRunsCount}"); Console.WriteLine($"Count of hot iterations: {HotRunsCount}"); Console.WriteLine(); var o = new object(); var rawType = o.GetType(); var ripeType = o.GetRipeType(); RunBenchmarks( (() => rawType.Name, "() => rawType.Name"), (() => ripeType.Name, "() => ripeType.Name"), (() => o.GetType().Name, "() => o.GetType().Name"), (() => o.GetRipeType().Name, "() => o.GetRipeType().Name") ); Console.WriteLine(); RunBenchmarks( (() => rawType.Assembly, "() => rawType.Assembly"), (() => ripeType.Assembly, "() => ripeType.Assembly"), (() => o.GetType().Assembly, "() => o.GetType().Assembly"), (() => o.GetRipeType().Assembly, "() => o.GetRipeType().Assembly") ); Console.WriteLine(); RunBenchmarks( (() => rawType.IsValueType, "() => rawType.IsValueType"), (() => ripeType.IsValueType, "() => ripeType.IsValueType"), (() => o.GetType().IsValueType, "() => o.GetType().IsValueType"), (() => o.GetRipeType().IsValueType, "() => o.GetRipeType().IsValueType") ); } static void TypeofVsTypeOf() { Console.WriteLine($"Count of warm iterations: {WarmRunsCount}"); Console.WriteLine($"Count of hot iterations: {HotRunsCount}"); Console.WriteLine(); RunBenchmarks( (() => typeof(int), "() => typeof(int)"), (() => TypeOf<int>.Raw, "() => TypeOf<int>.Raw"), (() => typeof(string), "() => typeof(string)"), (() => TypeOf<string>.Raw, "() => TypeOf<string>.Raw") ); Console.WriteLine(); RunBenchmarks( (() => typeof(int).Name, "() => typeof(int).Name"), (() => TypeOf<int>.Name, "() => TypeOf<int>.Name"), (() => typeof(string).Name, "() => typeof(string).Name"), (() => TypeOf<string>.Name, "() => TypeOf<string>.Name") ); Console.WriteLine(); RunBenchmarks( (() => typeof(int).Assembly, "() => typeof(int).Assembly"), (() => TypeOf<int>.Assembly, "() => TypeOf<int>.Assembly"), (() => typeof(string).Assembly, "() => typeof(string).Assembly"), (() => TypeOf<string>.Assembly, "() => TypeOf<string>.Assembly") ); Console.WriteLine(); RunBenchmarks( (() => typeof(int).IsValueType, "() => typeof(int).IsValueType"), (() => TypeOf<int>.IsValueType, "() => TypeOf<int>.IsValueType"), (() => typeof(string).IsValueType, "() => typeof(string).IsValueType"), (() => TypeOf<string>.IsValueType, "() => TypeOf<string>.IsValueType") ); } static void RunBenchmarks<T>(params (Func<T> Func, string StringRepresentation)[] funcAndViewTuples) => funcAndViewTuples .Select(t => ( BenchmarkResults: t.Func.InvokeBenchmark(HotRunsCount, WarmRunsCount), StringRepresentation: t.StringRepresentation)) .ToList().ForEach(t => Console.WriteLine( $"{t.StringRepresentation}\t{t.BenchmarkResults.Result}\t{t.BenchmarkResults.ElapsedMilliseconds} (ms)")); static (Func<T> Func, long ElapsedMilliseconds, T Result) InvokeBenchmark<T>(this Func<T> func, long hotRunsCount, long warmRunsCount) { var stopwatch = new Stopwatch(); var result = default(T); for (var i = 0L; i < warmRunsCount; i++) result = func(); stopwatch.Start(); for (var i = 0L; i < hotRunsCount; i++) result = func(); stopwatch.Stop(); return (func, stopwatch.ElapsedMilliseconds, result); } } }
Manual benchmarks - Results on Core CLR
Count of warm iterations: 1000 Count of hot iterations: 10000000 () => typeof(int) System.Int32 70 (ms) () => TypeOf<int>.Raw System.Int32 106 (ms) () => typeof(string) System.String 70 (ms) () => TypeOf<string>.Raw System.String 101 (ms) () => typeof(int).Name Int32 249 (ms) () => TypeOf<int>.Name Int32 42 (ms) () => typeof(string).Name String 245 (ms) () => TypeOf<string>.Name String 48 (ms) () => typeof(int).Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e 285 (ms) () => TypeOf<int>.Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e 42 (ms) () => typeof(string).Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e 340 (ms) () => TypeOf<string>.Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e 47 (ms) () => typeof(int).IsValueType True 544 (ms) () => TypeOf<int>.IsValueType True 53 (ms) () => typeof(string).IsValueType False 889 (ms) () => TypeOf<string>.IsValueType False 47 (ms) Count of warm iterations: 1000 Count of hot iterations: 10000000 () => rawType.Name Object 221 (ms) () => ripeType.Name Object 42 (ms) () => o.GetType().Name Object 250 (ms) () => o.GetRipeType().Name Object 687 (ms) () => rawType.Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e 271 (ms) () => ripeType.Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e 42 (ms) () => o.GetType().Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e 330 (ms) () => o.GetRipeType().Assembly System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e 686 (ms) () => rawType.IsValueType False 553 (ms) () => ripeType.IsValueType False 47 (ms) () => o.GetType().IsValueType False 590 (ms) () => o.GetRipeType().IsValueType False 711 (ms)
Заключение
`TypeOf` и `RipeType` паттерны позволяют ощутимо улучшить производительность множественных рекурсивных вызовов в некоторых сценариях на различных CLR.
