-
Notifications
You must be signed in to change notification settings - Fork 1
/
DefaultValue.cs
110 lines (99 loc) · 4.76 KB
/
DefaultValue.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
namespace Nucs.Reflection {
/// <summary>
/// A cached default value factory to type. also provides boxed of Type objects.
/// </summary>
public static class DefaultValue<T> {
private static readonly Func<T> _defaultNew;
/// <summary>
/// Value generated by default(T)
/// </summary>
public static readonly T? GetDefault = default(T);
/// <summary>
/// Boxing of default(T)
/// </summary>
#pragma warning disable CS8600
// ReSharper disable once RedundantCast
// ReSharper disable once StaticMemberInGenericType
public static readonly object? GetDefaultBoxed = (object) default(T);
#pragma warning restore CS8600
/// <summary>
/// Value generated by calling new T();
/// </summary>
public static T? GetDefaultNew =>
#if DEBUG
_defaultNew is null ? throw new ArgumentNullException() : _defaultNew();
#else
_defaultNew();
#endif
/// <summary>
/// Boxing of new T();
/// </summary>
public static object? GetDefaultNewBoxed =>
#if DEBUG
(object) _defaultNew is null ? throw new ArgumentNullException() : _defaultNew();
#else
(object) _defaultNew();
#endif
static DefaultValue() {
if (typeof(T).GetConstructor(Array.Empty<Type>()) is not null) {
_defaultNew = Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();
}
}
}
/// <summary>
/// A concurrently cached default value factory to type. also provides boxed of Type objects.
/// </summary>
public static class DefaultValue {
private static readonly ConcurrentDictionary<Type, object> _defaultValues = new ConcurrentDictionary<Type, object>();
/// <summary>
/// [ <c>public static object GetDefault(this Type type)</c> ]
/// <para></para>
/// Retrieves the default value for a given Type
/// </summary>
/// <param name="type">The Type for which to get the default value</param>
/// <returns>The default value for <paramref name="type"/></returns>
/// <remarks>
/// If a null Type, a reference Type, or a System.Void Type is supplied, this method always returns null. If a value type
/// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an
/// exception.
/// </remarks>
/// <example>
/// To use this method in its native, non-extension form, make a call like:
/// <code>
/// object Default = DefaultValue.GetDefault(someType);
/// </code>
/// To use this method in its Type-extension form, make a call like:
/// <code>
/// object Default = someType.GetDefault();
/// </code>
/// </example>
/// <seealso cref="GetDefault<T>"/>
public static object? GetDefault(this Type? type) {
return _defaultValues.GetOrAdd(type, type => {
// If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null
if (type is null || !type.IsValueType || type == typeof(void))
return null;
// If the supplied Type has generic parameters, its default value cannot be determined
if (type.ContainsGenericParameters)
throw new ArgumentException(
"{" + MethodBase.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
"> contains generic parameters, so the default value cannot be retrieved");
// If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct/enum), return a
// default instance of the value type
if (type.IsPrimitive || !type.IsNotPublic) {
try {
return Activator.CreateInstance(type);
} catch (Exception e) {
throw new ArgumentException($"{{{MethodBase.GetCurrentMethod()}}} Error:\n\nThe Activator.CreateInstance method could not create a default instance of the supplied value type <{type}> (Inner Exception message: \"{e.Message}\")", e);
}
}
// Fail with exception
throw new ArgumentException($"{{{MethodBase.GetCurrentMethod()}}} Error:\n\nThe supplied value type <{type}> is not a publicly-visible type, so the default value cannot be retrieved");
});
}
}
}