/
NullableConverter.cs
279 lines (244 loc) · 10.4 KB
/
NullableConverter.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
namespace System.ComponentModel
{
/// <summary>
/// TypeConverter to convert Nullable types to and from strings or the underlying simple type.
/// </summary>
public class NullableConverter : TypeConverter
{
private static readonly ConstructorInfo s_nullableConstructor = typeof(Nullable<>).GetConstructor(typeof(Nullable<>).GetGenericArguments())!;
/// <summary>
/// Nullable converter is initialized with the underlying simple type.
/// </summary>
[RequiresUnreferencedCode("The UnderlyingType cannot be statically discovered.")]
public NullableConverter(Type type)
{
NullableType = type;
UnderlyingType = Nullable.GetUnderlyingType(type)!;
if (UnderlyingType == null)
{
throw new ArgumentException(SR.NullableConverterBadCtorArg, nameof(type));
}
UnderlyingTypeConverter = TypeDescriptor.GetConverter(UnderlyingType);
}
/// <summary>
/// Gets a value indicating whether this converter can convert an object in the
/// given source type to the underlying simple type or a null.
/// </summary>
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
if (sourceType == UnderlyingType)
{
return true;
}
else if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.CanConvertFrom(context, sourceType);
}
return base.CanConvertFrom(context, sourceType);
}
/// <summary>
/// Converts the given value to the converter's underlying simple type or a null.
/// </summary>
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value)
{
if (value == null || value.GetType() == UnderlyingType)
{
return value;
}
else if (value is string && string.IsNullOrEmpty(value as string))
{
return null;
}
else if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.ConvertFrom(context, culture, value);
}
return base.ConvertFrom(context, culture, value);
}
/// <summary>
/// Gets a value indicating whether this converter can convert a value object to the destination type.
/// </summary>
public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType)
{
if (destinationType == UnderlyingType)
{
return true;
}
else if (destinationType == typeof(InstanceDescriptor))
{
return true;
}
else if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.CanConvertTo(context, destinationType);
}
return base.CanConvertTo(context, destinationType);
}
/// <summary>
/// Converts the given value object to the destination type.
/// </summary>
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
ArgumentNullException.ThrowIfNull(destinationType);
if (destinationType == UnderlyingType && value != null && NullableType.IsInstanceOfType(value))
{
return value;
}
else if (destinationType == typeof(InstanceDescriptor))
{
ConstructorInfo ci = (ConstructorInfo)NullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableConstructor);
Debug.Assert(ci != null, "Couldn't find constructor");
return new InstanceDescriptor(ci, new object?[] { value }, true);
}
else if (value == null)
{
// Handle our own nulls here
if (destinationType == typeof(string))
{
return string.Empty;
}
}
else if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.ConvertTo(context, culture, value, destinationType);
}
return base.ConvertTo(context, culture, value, destinationType);
}
/// <summary>
/// </summary>
public override object? CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues)
{
if (UnderlyingTypeConverter != null)
{
object? instance = UnderlyingTypeConverter.CreateInstance(context, propertyValues);
return instance;
}
return base.CreateInstance(context, propertyValues);
}
/// <summary>
/// Gets a value indicating whether changing a value on this object requires a call to
/// <see cref='System.ComponentModel.TypeConverter.CreateInstance(IDictionary)'/> to create a new value,
/// using the specified context.
/// </summary>
public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.GetCreateInstanceSupported(context);
}
return base.GetCreateInstanceSupported(context);
}
/// <summary>
/// Gets a collection of properties for the type of array specified by the value
/// parameter using the specified context and attributes.
/// </summary>
[RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)]
public override PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
{
if (UnderlyingTypeConverter != null)
{
object unwrappedValue = value;
return UnderlyingTypeConverter.GetProperties(context, unwrappedValue, attributes);
}
return base.GetProperties(context, value, attributes);
}
/// <summary>
/// Gets a value indicating whether this object supports properties using the specified context.
/// </summary>
public override bool GetPropertiesSupported(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.GetPropertiesSupported(context);
}
return base.GetPropertiesSupported(context);
}
/// <summary>
/// Gets a collection of standard values for the data type this type converter is designed for.
/// </summary>
public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
StandardValuesCollection? values = UnderlyingTypeConverter.GetStandardValues(context);
if (GetStandardValuesSupported(context) && values != null)
{
// Create a set of standard values around nullable instances.
object?[] wrappedValues = new object[values.Count + 1];
int idx = 0;
wrappedValues[idx++] = null;
foreach (object value in values)
{
wrappedValues[idx++] = value;
}
return new StandardValuesCollection(wrappedValues);
}
}
return base.GetStandardValues(context);
}
/// <summary>
/// Gets a value indicating whether the collection of standard values returned from
/// <see cref='System.ComponentModel.TypeConverter.GetStandardValues()'/> is an exclusive
/// list of possible values, using the specified context.
/// </summary>
public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.GetStandardValuesExclusive(context);
}
return base.GetStandardValuesExclusive(context);
}
/// <summary>
/// Gets a value indicating whether this object supports a standard set of values that can
/// be picked from a list using the specified context.
/// </summary>
public override bool GetStandardValuesSupported(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.GetStandardValuesSupported(context);
}
return base.GetStandardValuesSupported(context);
}
/// <summary>
/// Gets a value indicating whether the given value object is valid for this type.
/// </summary>
public override bool IsValid(ITypeDescriptorContext? context, object value)
{
if (UnderlyingTypeConverter != null)
{
object? unwrappedValue = value;
if (unwrappedValue == null)
{
return true; // null is valid for nullable.
}
else
{
return UnderlyingTypeConverter.IsValid(context, unwrappedValue);
}
}
return base.IsValid(context, value);
}
/// <summary>
/// The type this converter was initialized with.
/// </summary>
public Type NullableType { get; }
/// <summary>
/// The simple type that is represented as a nullable.
/// </summary>
public Type UnderlyingType { get; }
/// <summary>
/// Converter associated with the underlying simple type.
/// </summary>
public TypeConverter UnderlyingTypeConverter { get; }
}
}