• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dennisdoomen / reflectify / 31404809605

10 Aug 2026 03:39PM UTC coverage: 97.826% (+1.0%) from 96.835%
31404809605

Pull #174

github

web-flow
Merge 1d3822327 into 2ee555014
Pull Request #174: Expose nullable reference type metadata across all target frameworks

298 of 312 branches covered (95.51%)

Branch coverage included in aggregate %.

61 of 61 new or added lines in 3 files covered. (100.0%)

2 existing lines in 1 file now uncovered.

557 of 562 relevant lines covered (99.11%)

3914612.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.29
/src/Reflectify/TypeMetaDataExtensions.cs
1
#if !REFLECTIFY_COMPILE
2
// <autogenerated />
3
#pragma warning disable
4
#endif
5

6
#nullable disable
7

8
using System;
9
using System.Collections;
10
using System.Collections.Generic;
11
using System.Linq;
12
using System.Reflection;
13
using System.Runtime.CompilerServices;
14
using System.Text;
15
using System.Threading.Tasks;
16

17
namespace Reflectify;
18

19
#if REFLECTIFY_COMPILE
20
public static class TypeMetaDataExtensions
21
#else
22
[global::Microsoft.CodeAnalysis.Embedded]
23
[global::System.Diagnostics.DebuggerNonUserCode]
24
internal static class TypeMetaDataExtensions
25
#endif
26
{
27
    /// <summary>
28
    /// Returns the name of the type without the generic backtick and the type arguments.
29
    /// </summary>
30
    public static string GetNonGenericName(this Type type)
31
    {
8✔
32
        string name = type.Name;
8✔
33
        int index = name.IndexOf('`');
8✔
34
        return index == -1 ? name : name.Substring(0, index);
8✔
35
    }
8✔
36

37
    /// <summary>
38
    /// Returns <see langword="true" /> if the type is derived from an open-generic type, or <see langword="false" /> otherwise.
39
    /// </summary>
40
    public static bool IsDerivedFromOpenGeneric(this Type type, Type openGenericType)
41
    {
6✔
42
        // do not consider a type to be derived from itself
43
        if (type == openGenericType)
6✔
44
        {
2✔
45
            return false;
2✔
46
        }
47

48
        // check subject and its base types against definition
49
        for (Type baseType = type;
4✔
50
             baseType is not null;
10✔
51
             baseType = baseType.BaseType)
6✔
52
        {
8✔
53
            if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == openGenericType)
8✔
54
            {
2✔
55
                return true;
2✔
56
            }
57
        }
6✔
58

59
        return false;
2✔
60
    }
6✔
61

62
    /// <summary>
63
    /// Returns the interfaces that the <paramref name="type"/> implements or inherits from that are concrete
64
    /// versions of the <paramref name="openGenericType"/>.
65
    /// </summary>
66
    public static Type[] GetClosedGenericInterfaces(this Type type, Type openGenericType)
67
    {
36✔
68
        if (type.IsGenericType && type.GetGenericTypeDefinition() == openGenericType)
36✔
69
        {
4✔
70
            return [type];
4✔
71
        }
72

73
        Type[] interfaces = type.GetInterfaces();
32✔
74

75
        return interfaces
32✔
76
            .Where(t => t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == openGenericType))
306✔
77
            .ToArray();
32✔
78
    }
36✔
79

80
    /// <summary>
81
    /// Returns <see langword="true" /> if the type is decorated with the specific <typeparamref name="TAttribute"/>,
82
    /// or <see langword="false" /> otherwise.
83
    /// </summary>
84
    public static bool HasAttribute<TAttribute>(this Type type)
85
        where TAttribute : Attribute
86
    {
22✔
87
        return type.IsDefined(typeof(TAttribute), inherit: false);
22✔
88
    }
22✔
89

90
    /// <summary>
91
    /// Returns <see langword="true" /> if the type is decorated with the specific <typeparamref name="TAttribute"/> <i>and</i>
92
    /// that attribute instance matches the predicate, or <see langword="false" /> otherwise.
93
    /// </summary>
94
    public static bool HasAttribute<TAttribute>(this Type type, Func<TAttribute, bool> predicate)
95
        where TAttribute : Attribute
96
    {
10✔
97
        if (predicate is null)
10✔
98
        {
2✔
99
            throw new ArgumentNullException(nameof(predicate));
2✔
100
        }
101

102
        return type.GetCustomAttributes<TAttribute>(inherit: false).Any(predicate);
8✔
103
    }
8✔
104

105
    /// <summary>
106
    /// Returns <see langword="true" /> if the type or one its parents are decorated with the
107
    /// specific <typeparamref name="TAttribute"/>.
108
    /// </summary>
109
    public static bool HasAttributeInHierarchy<TAttribute>(this Type type)
110
        where TAttribute : Attribute
111
    {
6✔
112
        return type.IsDefined(typeof(TAttribute), inherit: true);
6✔
113
    }
6✔
114

115
    /// <summary>
116
    /// Returns <see langword="true" /> if the type or one its parents are decorated with the
117
    /// specific <typeparamref name="TAttribute"/> <i>and</i> that attribute instance
118
    /// matches the predicate. Returns <see langword="false" /> otherwise.
119
    /// </summary>
120
    public static bool HasAttributeInHierarchy<TAttribute>(this Type type, Func<TAttribute, bool> predicate)
121
        where TAttribute : Attribute
122
    {
8✔
123
        if (predicate is null)
8✔
124
        {
4✔
125
            throw new ArgumentNullException(nameof(predicate));
4✔
126
        }
127

128
        return type.GetCustomAttributes<TAttribute>(inherit: true).Any(predicate);
4✔
129
    }
4✔
130

131
    /// <summary>
132
    /// Retrieves all custom attributes of the specified type from a class or its inheritance hierarchy.
133
    /// </summary>
134
    public static TAttribute[] GetMatchingAttributes<TAttribute>(this Type type)
135
        where TAttribute : Attribute
136
    {
4✔
137
        return (TAttribute[])type.GetCustomAttributes<TAttribute>(inherit: true);
4✔
138
    }
4✔
139

140
    /// <summary>
141
    /// Retrieves an array of attributes of a specified type that match the provided predicate.
142
    /// </summary>
143
    public static TAttribute[] GetMatchingAttributes<TAttribute>(this Type type, Func<TAttribute, bool> predicate)
144
        where TAttribute : Attribute
145
    {
8✔
146
        if (predicate is null)
8!
UNCOV
147
        {
×
UNCOV
148
            throw new ArgumentNullException(nameof(predicate));
×
149
        }
150

151
        return type.GetCustomAttributes<TAttribute>(inherit: true).Where(predicate).ToArray();
8✔
152
    }
8✔
153

154
    /// <summary>
155
    /// Returns <see langword="true" /> if the type overrides the Equals method, or <see langword="false" /> otherwise.
156
    /// </summary>
157
    public static bool OverridesEquals(this Type type)
158
    {
4✔
159
        MethodInfo method = type
4✔
160
            .GetMethod("Equals", [typeof(object)]);
4✔
161

162
        return method is not null
4!
163
               && method.GetBaseDefinition().DeclaringType != method.DeclaringType;
4✔
164
    }
4✔
165

166
    /// <summary>
167
    /// Determines whether the actual type is the same as, or inherits from, the expected type.
168
    /// </summary>
169
    /// <remarks>
170
    /// The expected type can also be an open generic type definition.
171
    /// </remarks>
172
    /// <returns><see langword="true" /> if the actual type is the same as, or inherits from, the expected type; otherwise, <see langword="false" />.</returns>
173
    public static bool IsSameOrInherits<TExpectedType>(this Type actualType)
174
    {
12✔
175
        return actualType.IsSameOrInherits(typeof(TExpectedType));
12✔
176
    }
12✔
177

178
    /// <summary>
179
    /// Determines whether the actual type is the same as, or inherits from, the expected type.
180
    /// </summary>
181
    /// <remarks>
182
    /// The expected type can also be an open generic type definition.
183
    /// </remarks>
184
    /// <returns><see langword="true" /> if the actual type is the same as, or inherits from, the expected type; otherwise, <see langword="false" />.</returns>
185
    public static bool IsSameOrInherits(this Type actualType, Type expectedType)
186
    {
24✔
187
        return actualType == expectedType ||
24✔
188
               expectedType.IsAssignableFrom(actualType) ||
24✔
189
               (actualType.BaseType is { IsGenericType: true } && actualType.BaseType.GetGenericTypeDefinition() == expectedType);
24✔
190
    }
24✔
191

192
    /// <summary>
193
    /// Returns <see langword="true" /> if the type is a delegate type; otherwise, <see langword="false" />.
194
    /// </summary>
195
    public static bool IsDelegate(this Type type)
196
    {
6✔
197
        return typeof(Delegate).IsAssignableFrom(type);
6✔
198
    }
6✔
199

200
    /// <summary>
201
    /// Returns <see langword="true" /> if the type is a compiler-generated type, or <see langword="false" /> otherwise.
202
    /// </summary>
203
    /// <remarks>
204
    /// Typical examples of compiler-generated types are anonymous types, tuples, and records.
205
    /// </remarks>
206
    public static bool IsCompilerGenerated(this Type type)
207
    {
10✔
208
        return type.HasAttribute<CompilerGeneratedAttribute>() ||
10✔
209
               type.IsRecord() ||
10✔
210
               type.IsTuple();
10✔
211
    }
10✔
212

213
    /// <summary>
214
    /// Returns <see langword="true" /> if the type has a readable name, or <see langword="false" />
215
    /// if it is a compiler-generated name.
216
    /// </summary>
217
    public static bool HasFriendlyName(this Type type)
218
    {
6✔
219
        return !type.IsAnonymous() && !type.IsTuple();
6✔
220
    }
6✔
221

222
    /// <summary>
223
    /// Return <see langword="true" /> if the type is a tuple type; otherwise, <see langword="false" />
224
    /// </summary>
225
    public static bool IsTuple(this Type type)
226
    {
14✔
227
        if (!type.IsGenericType)
14✔
228
        {
6✔
229
            return false;
6✔
230
        }
231

232
#if NETCOREAPP2_0_OR_GREATER || NET471_OR_GREATER || NETSTANDARD2_1_OR_GREATER
233
        return typeof(ITuple).IsAssignableFrom(type);
8✔
234
#else
235
        Type openType = type.GetGenericTypeDefinition();
236

237
        return openType == typeof(ValueTuple<>)
238
               || openType == typeof(ValueTuple<,>)
239
               || openType == typeof(ValueTuple<,,>)
240
               || openType == typeof(ValueTuple<,,,>)
241
               || openType == typeof(ValueTuple<,,,,>)
242
               || openType == typeof(ValueTuple<,,,,,>)
243
               || openType == typeof(ValueTuple<,,,,,,>)
244
               || (openType == typeof(ValueTuple<,,,,,,,>) && IsTuple(type.GetGenericArguments()[7]))
245
               || openType == typeof(Tuple<>)
246
               || openType == typeof(Tuple<,>)
247
               || openType == typeof(Tuple<,,>)
248
               || openType == typeof(Tuple<,,,>)
249
               || openType == typeof(Tuple<,,,,>)
250
               || openType == typeof(Tuple<,,,,,>)
251
               || openType == typeof(Tuple<,,,,,,>)
252
               || (openType == typeof(Tuple<,,,,,,,>) && IsTuple(type.GetGenericArguments()[7]));
253
#endif
254
    }
14✔
255

256
    /// <summary>
257
    /// Returns <see langword="true" /> if the type is an anonymous type, or <see langword="false" /> otherwise.
258
    /// </summary>
259
    public static bool IsAnonymous(this Type type)
260
    {
12✔
261
        if (type.FullName!.IndexOf("AnonymousType", StringComparison.Ordinal) < 0)
12✔
262
        {
8✔
263
            return false;
8✔
264
        }
265

266
        return type.HasAttribute<CompilerGeneratedAttribute>();
4✔
267
    }
12✔
268

269
    /// <summary>
270
    /// Return <see langword="true" /> if the type is a struct or class record type; otherwise, <see langword="false" />.
271
    /// </summary>
272
    public static bool IsRecord(this Type type)
273
    {
12✔
274
        return type.IsRecordClass() || type.IsRecordStruct();
12✔
275
    }
12✔
276

277
    /// <summary>
278
    /// Returns <see langword="true" /> if the type is a class record type; otherwise, <see langword="false" />.
279
    /// </summary>
280
    public static bool IsRecordClass(this Type type)
281
    {
18✔
282
        return type.GetMethod("<Clone>$", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly) is { } &&
18!
283
               type.GetProperty("EqualityContract", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)?
18✔
284
                   .GetMethod?.HasAttribute<CompilerGeneratedAttribute>() == true;
18✔
285
    }
18✔
286

287
    /// <summary>
288
    /// Return <see langword="true" /> if the type is a record struct; otherwise, <see langword="false" />
289
    /// </summary>
290
    public static bool IsRecordStruct(this Type type)
291
    {
16✔
292
        // As noted here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/record-structs#open-questions
293
        // recognizing record structs from metadata is an open point. The following check is based on common sense
294
        // and heuristic testing, apparently giving good results but not supported by official documentation.
295
        return type.BaseType == typeof(ValueType) &&
16!
296
               type.GetMethod("PrintMembers", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly, null,
16✔
297
                   [typeof(StringBuilder)], null) is { } &&
16✔
298
               type.GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly, null,
16✔
299
                       [type, type], null)?
16✔
300
                   .HasAttribute<CompilerGeneratedAttribute>() == true;
16✔
301
    }
16✔
302

303
    /// <summary>
304
    /// Determines whether the specified type is a KeyValuePair.
305
    /// </summary>
306
    /// <param name="type">The type to check.</param>
307
    /// <returns><see langword="true" /> if the type is a KeyValuePair; otherwise, <see langword="false" />.</returns>
308
    public static bool IsKeyValuePair(this Type type)
309
    {
14✔
310
        return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>);
14✔
311
    }
14✔
312

313
    /// <summary>
314
    /// Returns <see langword="true" /> if the type is a struct (i.e., a value type that is not an enum);
315
    /// otherwise, <see langword="false" />.
316
    /// </summary>
317
    public static bool IsStruct(this Type type)
318
    {
8✔
319
        return type.IsValueType && !type.IsEnum;
8✔
320
    }
8✔
321

322
    /// <summary>
323
    /// Returns <see langword="true" /> if the type is a ref struct; otherwise, <see langword="false" />.
324
    /// </summary>
325
    /// <remarks>
326
    /// Returns <see langword="false" /> on .NET versions that do not support ref structs.
327
    /// </remarks>
328
    public static bool IsRefStruct(this Type type)
329
    {
8✔
330
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
331
        return type.IsDefined(typeof(IsByRefLikeAttribute), false);
8✔
332
#else
333
        return false;
334
#endif
335
    }
8✔
336

337
    /// <summary>
338
    /// Returns <see langword="true" /> if the type is a constructed <see cref="Nullable{T}"/> type,
339
    /// or <see langword="false" /> otherwise.
340
    /// </summary>
341
    /// <remarks>
342
    /// This uses the same check as <c>NullableOrActualType</c> (<c>type.IsGenericType &amp;&amp; type.GetGenericTypeDefinition() == typeof(Nullable&lt;&gt;)</c>),
343
    /// so <c>type.IsNullable()</c> is <see langword="true" /> exactly when <c>type.NullableOrActualType()</c> returns a
344
    /// different type than <paramref name="type"/> itself.
345
    /// </remarks>
346
    public static bool IsNullable(this Type type)
347
    {
6✔
348
        return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
6✔
349
    }
6✔
350

351
    /// <summary>
352
    /// Returns <see langword="true" /> if the type implements <see cref="IEnumerable"/> (including <see cref="IEnumerable{T}"/>),
353
    /// or <see langword="false" /> otherwise.
354
    /// </summary>
355
    /// <remarks>
356
    /// <see cref="string"/> is explicitly excluded, even though it implements <see cref="IEnumerable{T}"/> of <see cref="char"/>.
357
    /// Treating strings as enumerables is rarely what a caller wants, so this method always returns <see langword="false" />
358
    /// for <see cref="string"/>.
359
    /// </remarks>
360
    /// <remarks>
361
    /// This method only recognizes the standard <see cref="IEnumerable"/>/<see cref="IEnumerable{T}"/> interfaces.
362
    /// It does not support the C# 9 pattern-based (duck-typed) <c>foreach</c>, i.e. a type that merely exposes a
363
    /// public <c>GetEnumerator()</c> method without implementing <see cref="IEnumerable"/>. Recognizing the
364
    /// well-known interfaces covers the vast majority of real-world use cases.
365
    /// </remarks>
366
    public static bool IsEnumerable(this Type type)
367
    {
22✔
368
        return type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type);
22✔
369
    }
22✔
370

371
    /// <summary>
372
    /// Returns the element type of an enumerable <paramref name="type"/>, or <see langword="null" /> if the type
373
    /// is not enumerable.
374
    /// </summary>
375
    /// <remarks>
376
    /// For arrays, this returns the array's element type. For types that implement <see cref="IEnumerable{T}"/>
377
    /// exactly once (directly or through an inherited interface), this returns that single <c>T</c>. When a type
378
    /// implements <see cref="IEnumerable{T}"/> for multiple different element types (e.g. a type that implements
379
    /// both <c>IEnumerable&lt;int&gt;</c> and <c>IEnumerable&lt;string&gt;</c>), or when the type only implements the
380
    /// non-generic <see cref="IEnumerable"/>, the element type cannot be uniquely determined. In that case, this
381
    /// method falls back to returning <see cref="object"/> as a safe default.
382
    /// </remarks>
383
    public static Type GetElementTypeOfEnumerable(this Type type)
384
    {
12✔
385
        if (!type.IsEnumerable())
12✔
386
        {
4✔
387
            return null;
4✔
388
        }
389

390
        if (type.IsArray)
8✔
391
        {
2✔
392
            return type.GetElementType();
2✔
393
        }
394

395
        Type[] elementTypes = type.GetClosedGenericInterfacesIncludingSelf(typeof(IEnumerable<>))
6✔
396
            .Select(i => i.GetGenericArguments()[0])
14✔
397
            .Distinct()
6✔
398
            .ToArray();
6✔
399

400
        return elementTypes.Length == 1 ? elementTypes[0] : typeof(object);
6✔
401
    }
12✔
402

403
    /// <summary>
404
    /// Returns <see langword="true" /> if the type implements <see cref="IDictionary"/> or a closed
405
    /// <see cref="IDictionary{TKey,TValue}"/> or <see cref="IReadOnlyDictionary{TKey,TValue}"/>, or <see langword="false" /> otherwise.
406
    /// </summary>
407
    public static bool IsDictionary(this Type type)
408
    {
8✔
409
        return typeof(IDictionary).IsAssignableFrom(type)
8✔
410
               || type.GetClosedGenericInterfacesIncludingSelf(typeof(IDictionary<,>)).Length > 0
8✔
411
               || type.GetClosedGenericInterfacesIncludingSelf(typeof(IReadOnlyDictionary<,>)).Length > 0;
8✔
412
    }
8✔
413

414
    /// <summary>
415
    /// Attempts to get the key and value types of a dictionary <paramref name="type"/>.
416
    /// </summary>
417
    /// <returns>
418
    /// <see langword="true" /> and the key and value types if <paramref name="type"/> implements a closed
419
    /// <see cref="IDictionary{TKey,TValue}"/> or <see cref="IReadOnlyDictionary{TKey,TValue}"/>; otherwise,
420
    /// <see langword="false" /> with both out parameters set to <see langword="null" />.
421
    /// </returns>
422
    /// <remarks>
423
    /// Types that only implement the non-generic <see cref="IDictionary"/> (and hence do return
424
    /// <see langword="true" /> from <see cref="IsDictionary"/>) do not carry key/value type information and will
425
    /// therefore make this method return <see langword="false" />.
426
    /// </remarks>
427
    public static bool TryGetDictionaryTypes(this Type type, out Type keyType, out Type valueType)
428
    {
6✔
429
        Type match = type.GetClosedGenericInterfacesIncludingSelf(typeof(IDictionary<,>)).FirstOrDefault()
6✔
430
                      ?? type.GetClosedGenericInterfacesIncludingSelf(typeof(IReadOnlyDictionary<,>)).FirstOrDefault();
6✔
431

432
        if (match is not null)
6✔
433
        {
4✔
434
            Type[] genericArguments = match.GetGenericArguments();
4✔
435
            keyType = genericArguments[0];
4✔
436
            valueType = genericArguments[1];
4✔
437
            return true;
4✔
438
        }
439

440
        keyType = null;
2✔
441
        valueType = null;
2✔
442
        return false;
2✔
443
    }
6✔
444

445
    /// <summary>
446
    /// Returns <see langword="true" /> if the type can be awaited using the C# <c>await</c> keyword, or
447
    /// <see langword="false" /> otherwise.
448
    /// </summary>
449
    /// <remarks>
450
    /// This is a reflection-based duck-typing check that mirrors what the C# compiler itself requires of an
451
    /// awaitable type: a public, parameterless, instance <c>GetAwaiter()</c> method whose return type (the
452
    /// "awaiter") has a public <c>IsCompleted</c> property, a public parameterless <c>GetResult()</c> method, and
453
    /// implements <see cref="INotifyCompletion"/>. The awaiter type itself does not need to come from a specific
454
    /// namespace or assembly; any type that satisfies this shape is recognized.
455
    /// </remarks>
456
    public static bool IsAwaitable(this Type type)
457
    {
12✔
458
        MethodInfo getAwaiter = type.GetMethod("GetAwaiter", BindingFlags.Public | BindingFlags.Instance, null,
12✔
459
            Type.EmptyTypes, null);
12✔
460

461
        if (getAwaiter is null)
12✔
462
        {
2✔
463
            return false;
2✔
464
        }
465

466
        Type awaiterType = getAwaiter.ReturnType;
10✔
467

468
        bool hasIsCompleted = awaiterType.GetProperty("IsCompleted", BindingFlags.Public | BindingFlags.Instance)
10!
469
            ?.GetMethod?.IsPublic == true;
10✔
470

471
        bool hasGetResult = awaiterType.GetMethod("GetResult", BindingFlags.Public | BindingFlags.Instance, null,
10✔
472
            Type.EmptyTypes, null) is not null;
10✔
473

474
        bool implementsNotifyCompletion = typeof(INotifyCompletion).IsAssignableFrom(awaiterType);
10✔
475

476
        return hasIsCompleted && hasGetResult && implementsNotifyCompletion;
10✔
477
    }
12✔
478

479
    /// <summary>
480
    /// Returns <see langword="true" /> if the type is <see cref="Task"/>, a constructed <see cref="Task{TResult}"/>,
481
    /// <c>ValueTask</c>, or a constructed <c>ValueTask&lt;TResult&gt;</c>, or <see langword="false" /> otherwise.
482
    /// </summary>
483
    /// <remarks>
484
    /// Unlike <see cref="IsAwaitable"/>, this is a narrower check for these specific, concrete BCL types rather than
485
    /// a general awaitable duck-type check. <c>ValueTask</c> and <c>ValueTask&lt;TResult&gt;</c> are not part
486
    /// of the reference assemblies of every target framework this library supports (on net47 and netstandard2.0 they
487
    /// are only available through the <c>System.Threading.Tasks.Extensions</c> package), so they are recognized by
488
    /// their full name rather than through a direct <c>typeof</c> reference, keeping this method available - and
489
    /// correct - on every supported target framework regardless of which packages the consumer has installed.
490
    /// </remarks>
491
    public static bool IsTaskLike(this Type type)
492
    {
12✔
493
        if (type == typeof(Task) || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>)))
12✔
494
        {
4✔
495
            return true;
4✔
496
        }
497

498
        if (type.FullName == "System.Threading.Tasks.ValueTask")
8✔
499
        {
2✔
500
            return true;
2✔
501
        }
502

503
        if (type.IsGenericType && type.GetGenericTypeDefinition().FullName == "System.Threading.Tasks.ValueTask`1")
6✔
504
        {
2✔
505
            return true;
2✔
506
        }
507

508
        return false;
4✔
509
    }
12✔
510

511
    /// <summary>
512
    /// Returns <see langword="true" /> if the type is one of the numeric primitive types (<see cref="byte"/>,
513
    /// <see cref="sbyte"/>, <see cref="short"/>, <see cref="ushort"/>, <see cref="int"/>, <see cref="uint"/>,
514
    /// <see cref="long"/>, <see cref="ulong"/>, <see cref="float"/>, <see cref="double"/>, or <see cref="decimal"/>),
515
    /// or <see langword="false" /> otherwise.
516
    /// </summary>
517
    /// <remarks>
518
    /// <see cref="decimal"/> is included explicitly because, unlike the other numeric types, it is not considered
519
    /// a CLR primitive (<c>typeof(decimal).IsPrimitive</c> is <see langword="false" />).
520
    /// </remarks>
521
    /// <remarks>
522
    /// <see cref="Nullable{T}"/> forms of these types (e.g. <c>int?</c>) are considered <b>not</b> numeric by this
523
    /// method; the check applies to the type itself only. Callers that want nullable-aware numeric checks should
524
    /// first unwrap the type, for instance using <c>NullableOrActualType</c>.
525
    /// </remarks>
526
    public static bool IsNumeric(this Type type)
527
    {
32✔
528
        return type == typeof(byte)
32✔
529
               || type == typeof(sbyte)
32✔
530
               || type == typeof(short)
32✔
531
               || type == typeof(ushort)
32✔
532
               || type == typeof(int)
32✔
533
               || type == typeof(uint)
32✔
534
               || type == typeof(long)
32✔
535
               || type == typeof(ulong)
32✔
536
               || type == typeof(float)
32✔
537
               || type == typeof(double)
32✔
538
               || type == typeof(decimal);
32✔
539
    }
32✔
540

541
    /// <summary>
542
    /// Returns <see langword="true" /> if the type is a CLR primitive type or <see cref="string"/>, or
543
    /// <see langword="false" /> otherwise.
544
    /// </summary>
545
    /// <remarks>
546
    /// This follows the underlying <see cref="Type.IsPrimitive"/> CLR semantics as-is, which has a couple of
547
    /// notable quirks: <see cref="decimal"/> is <b>not</b> considered primitive (see <see cref="IsNumeric"/> if you
548
    /// need to include it), while <see cref="IntPtr"/> and <see cref="UIntPtr"/> <b>are</b> considered primitive on
549
    /// some but not all frameworks/runtimes. This method intentionally does not attempt to normalize that
550
    /// framework-dependent behavior and simply defers to <see cref="Type.IsPrimitive"/>.
551
    /// </remarks>
552
    public static bool IsPrimitiveOrString(this Type type)
553
    {
16✔
554
        return type.IsPrimitive || type == typeof(string);
16✔
555
    }
16✔
556

557
    /// <summary>
558
    /// Returns the closed generic interfaces implemented by <paramref name="type"/> that close over
559
    /// <paramref name="openGenericType"/>, adapting <see cref="GetClosedGenericInterfaces"/> to also cover the case
560
    /// where <paramref name="type"/> itself, or one of the interfaces it directly implements, is already a closed
561
    /// version of <paramref name="openGenericType"/> (a case <see cref="GetClosedGenericInterfaces"/> does not
562
    /// cover on its own, since it only looks for the open generic type among the interfaces of the interfaces
563
    /// implemented by <paramref name="type"/>).
564
    /// </summary>
565
    private static Type[] GetClosedGenericInterfacesIncludingSelf(this Type type, Type openGenericType)
566
    {
28✔
567
        IEnumerable<Type> self = type.IsGenericType && type.GetGenericTypeDefinition() == openGenericType
28✔
568
            ? [type]
28✔
569
            : Type.EmptyTypes;
28✔
570

571
        IEnumerable<Type> directInterfaces = type.GetInterfaces()
28✔
572
            .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == openGenericType);
160✔
573

574
        return self
28✔
575
            .Concat(directInterfaces)
28✔
576
            .Concat(type.GetClosedGenericInterfaces(openGenericType))
28✔
577
            .Distinct()
28✔
578
            .ToArray();
28✔
579
    }
28✔
580
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc