@@ -4,6 +4,7 @@ namespace Schema.NET
44 using System . Collections ;
55 using System . Collections . Generic ;
66 using System . Linq ;
7+ using System . Runtime . CompilerServices ;
78
89 /// <summary>
910 /// A single or list of values.
@@ -15,131 +16,134 @@ public readonly struct OneOrMany<T>
1516 : IReadOnlyCollection < T > , IEnumerable < T > , IValues , IEquatable < OneOrMany < T > >
1617#pragma warning restore CA1710 // Identifiers should have correct suffix
1718 {
18- private readonly List < T > collection ;
19- private readonly T item ;
19+ private readonly T [ ] collection ;
2020
2121 /// <summary>
2222 /// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
2323 /// </summary>
2424 /// <param name="item">The single item value.</param>
2525 public OneOrMany ( T item )
2626 {
27- this . collection = null ;
28- this . item = item ;
27+ if ( item is null || ( item is string itemAsString && string . IsNullOrWhiteSpace ( itemAsString ) ) )
28+ {
29+ this . collection = null ;
30+ this . HasOne = false ;
31+ }
32+ else
33+ {
34+ this . collection = new [ ] { item } ;
35+ this . HasOne = true ;
36+ }
2937 }
3038
3139 /// <summary>
3240 /// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
3341 /// </summary>
34- /// <param name="array">The array of values.</param>
35- public OneOrMany ( params T [ ] array )
36- : this ( array is null ? null : new List < T > ( array ) )
42+ /// <param name="span">The span of values.</param>
43+ public OneOrMany ( ReadOnlySpan < T > span )
3744 {
45+ if ( ! span . IsEmpty )
46+ {
47+ var items = new T [ span . Length ] ;
48+ var index = 0 ;
49+
50+ if ( typeof ( T ) == typeof ( string ) )
51+ {
52+ for ( var i = 0 ; i < span . Length ; i ++ )
53+ {
54+ var item = span [ i ] ;
55+ if ( ! string . IsNullOrWhiteSpace ( item as string ) )
56+ {
57+ items [ index ] = item ;
58+ index ++ ;
59+ }
60+ }
61+ }
62+ else
63+ {
64+ for ( var i = 0 ; i < span . Length ; i ++ )
65+ {
66+ var item = span [ i ] ;
67+ if ( item != null )
68+ {
69+ items [ index ] = item ;
70+ index ++ ;
71+ }
72+ }
73+ }
74+
75+ if ( index > 0 )
76+ {
77+ if ( index == span . Length )
78+ {
79+ this . collection = items ;
80+ }
81+ else
82+ {
83+ this . collection = new T [ index ] ;
84+ Array . Copy ( items , 0 , this . collection , 0 , index ) ;
85+ }
86+
87+ this . HasOne = index == 1 ;
88+ return ;
89+ }
90+ }
91+
92+ this . collection = null ;
93+ this . HasOne = false ;
3894 }
3995
4096 /// <summary>
4197 /// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
4298 /// </summary>
43- /// <param name="collection ">The collection of values.</param>
44- public OneOrMany ( IEnumerable < T > collection )
45- : this ( collection is null ? null : new List < T > ( collection ) )
99+ /// <param name="array ">The array of values.</param>
100+ public OneOrMany ( params T [ ] array )
101+ : this ( array . AsSpan ( ) )
46102 {
47103 }
48104
49105 /// <summary>
50106 /// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
51107 /// </summary>
52- /// <param name="list">The list of values.</param>
53- public OneOrMany ( List < T > list )
108+ /// <param name="collection">The collection of values.</param>
109+ public OneOrMany ( IEnumerable < T > collection )
110+ : this ( collection . ToArray ( ) . AsSpan ( ) )
54111 {
55- if ( list is null )
56- {
57- throw new ArgumentNullException ( nameof ( list ) ) ;
58- }
59-
60- if ( list . Count == 1 )
61- {
62- this . collection = null ;
63- this . item = list [ 0 ] ;
64- }
65- else
66- {
67- this . collection = list ;
68- this . item = default ;
69- }
70112 }
71113
72114 /// <summary>
73115 /// Initializes a new instance of the <see cref="OneOrMany{T}"/> struct.
74116 /// </summary>
75- /// <param name="list">The list of values.</param>
76- public OneOrMany ( IEnumerable < object > list )
117+ /// <param name="collection">The list of values.</param>
118+ public OneOrMany ( IEnumerable < object > collection )
119+ : this ( collection . Cast < T > ( ) . ToArray ( ) . AsSpan ( ) )
77120 {
78- if ( list is null )
79- {
80- throw new ArgumentNullException ( nameof ( list ) ) ;
81- }
82-
83- var items = new List < T > ( ) ;
84- foreach ( var item in list )
85- {
86- if ( item is T itemT )
87- {
88- items . Add ( itemT ) ;
89- }
90- }
91-
92- if ( items . Count == 1 )
93- {
94- this . collection = null ;
95- this . item = items [ 0 ] ;
96- }
97- else
98- {
99- this . collection = items ;
100- this . item = default ;
101- }
102121 }
103122
104123 /// <summary>
105124 /// Gets the number of elements contained in the <see cref="OneOrMany{T}"/>.
106125 /// </summary>
107- public int Count
108- {
109- get
110- {
111- if ( this . HasOne )
112- {
113- return 1 ;
114- }
115- else if ( this . HasMany )
116- {
117- return this . collection . Count ;
118- }
119-
120- return 0 ;
121- }
122- }
126+ public int Count => this . collection ? . Length ?? 0 ;
123127
124128 /// <summary>
125129 /// Gets a value indicating whether this instance has a single item value.
126130 /// </summary>
127131 /// <value><c>true</c> if this instance has a single item value; otherwise, <c>false</c>.</value>
128- public bool HasOne => this . collection is null && this . item != null ;
132+ public bool HasOne { get ; }
129133
130134 /// <summary>
131135 /// Gets a value indicating whether this instance has more than one value.
132136 /// </summary>
133137 /// <value><c>true</c> if this instance has more than one value; otherwise, <c>false</c>.</value>
134- public bool HasMany => this . collection != null ;
138+ public bool HasMany => this . collection ? . Length > 1 ;
135139
136140 /// <summary>
137141 /// Performs an implicit conversion from <typeparamref name="T"/> to <see cref="OneOrMany{T}"/>.
138142 /// </summary>
139143 /// <param name="item">The single item value.</param>
140144 /// <returns>The result of the conversion.</returns>
141145#pragma warning disable CA2225 // Operator overloads have named alternates
142- public static implicit operator OneOrMany < T > ( T item ) => item != null && IsStringNullOrWhiteSpace ( item ) ? default : new OneOrMany < T > ( item ) ;
146+ public static implicit operator OneOrMany < T > ( T item ) => new OneOrMany < T > ( item ) ;
143147#pragma warning restore CA2225 // Operator overloads have named alternates
144148
145149 /// <summary>
@@ -148,7 +152,7 @@ public int Count
148152 /// <param name="array">The array of values.</param>
149153 /// <returns>The result of the conversion.</returns>
150154#pragma warning disable CA2225 // Operator overloads have named alternates
151- public static implicit operator OneOrMany < T > ( T [ ] array ) => new OneOrMany < T > ( array ? . Where ( x => x != null && ! IsStringNullOrWhiteSpace ( x ) ) ) ;
155+ public static implicit operator OneOrMany < T > ( T [ ] array ) => new OneOrMany < T > ( array ) ;
152156#pragma warning restore CA2225 // Operator overloads have named alternates
153157
154158 /// <summary>
@@ -157,7 +161,7 @@ public int Count
157161 /// <param name="list">The list of values.</param>
158162 /// <returns>The result of the conversion.</returns>
159163#pragma warning disable CA2225 // Operator overloads have named alternates
160- public static implicit operator OneOrMany < T > ( List < T > list ) => new OneOrMany < T > ( list ? . Where ( x => x != null && ! IsStringNullOrWhiteSpace ( x ) ) ) ;
164+ public static implicit operator OneOrMany < T > ( List < T > list ) => new OneOrMany < T > ( list ) ;
161165#pragma warning restore CA2225 // Operator overloads have named alternates
162166
163167 /// <summary>
@@ -219,17 +223,13 @@ public int Count
219223 /// <returns>An enumerator for the <see cref="OneOrMany{T}"/>.</returns>
220224 public IEnumerator < T > GetEnumerator ( )
221225 {
222- if ( this . HasMany )
226+ if ( this . collection != null )
223227 {
224- foreach ( var item in this . collection )
228+ for ( var i = 0 ; i < this . collection . Length ; i ++ )
225229 {
226- yield return item ;
230+ yield return this . collection [ i ] ;
227231 }
228232 }
229- else if ( this . HasOne )
230- {
231- yield return this . item ;
232- }
233233 }
234234
235235 /// <summary>
@@ -238,6 +238,32 @@ public IEnumerator<T> GetEnumerator()
238238 /// <returns>An enumerator for the <see cref="OneOrMany{T}"/>.</returns>
239239 IEnumerator IEnumerable . GetEnumerator ( ) => this . GetEnumerator ( ) ;
240240
241+ /// <summary>
242+ /// Creates an array from <see cref="OneOrMany{T}" />.
243+ /// </summary>
244+ /// <returns>An array containing all the elements.</returns>
245+ public T [ ] ToArray ( )
246+ {
247+ if ( this . HasOne )
248+ {
249+ return new [ ] { this . collection [ 0 ] } ;
250+ }
251+ else if ( this . HasMany )
252+ {
253+ var result = new T [ this . collection . Length ] ;
254+ Array . Copy ( this . collection , 0 , result , 0 , this . collection . Length ) ;
255+ return result ;
256+ }
257+ else
258+ {
259+ #if NETSTANDARD1_1
260+ return new T [ 0 ] ;
261+ #else
262+ return Array . Empty < T > ( ) ;
263+ #endif
264+ }
265+ }
266+
241267 /// <summary>
242268 /// Indicates whether the current object is equal to another object of the same type.
243269 /// </summary>
@@ -253,16 +279,16 @@ public bool Equals(OneOrMany<T> other)
253279 }
254280 else if ( this . HasOne && other . HasOne )
255281 {
256- return this . item . Equals ( other . item ) ;
282+ return this . collection [ 0 ] . Equals ( other . collection [ 0 ] ) ;
257283 }
258284 else if ( this . HasMany && other . HasMany )
259285 {
260- if ( this . collection . Count != other . collection . Count )
286+ if ( this . collection . Length != other . collection . Length )
261287 {
262288 return false ;
263289 }
264290
265- for ( var i = 0 ; i < this . collection . Count ; i ++ )
291+ for ( var i = 0 ; i < this . collection . Length ; i ++ )
266292 {
267293 if ( ! EqualityComparer < T > . Default . Equals ( this . collection [ i ] , other . collection [ i ] ) )
268294 {
@@ -291,26 +317,6 @@ public bool Equals(OneOrMany<T> other)
291317 /// <returns>
292318 /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
293319 /// </returns>
294- public override int GetHashCode ( )
295- {
296- if ( this . HasOne )
297- {
298- return HashCode . Of ( this . item ) ;
299- }
300- else if ( this . HasMany )
301- {
302- return HashCode . OfEach ( this . collection ) ;
303- }
304-
305- return 0 ;
306- }
307-
308- /// <summary>
309- /// Checks whether the generic T item is a string that is either null or contains whitespace.
310- /// </summary>
311- /// <returns>
312- /// Returns true if the supplied item is a string that is null or contains whitespace.
313- /// </returns>
314- private static bool IsStringNullOrWhiteSpace ( T item ) => item . GetType ( ) == typeof ( string ) && string . IsNullOrWhiteSpace ( item as string ) ;
320+ public override int GetHashCode ( ) => HashCode . OfEach ( this . collection ) ;
315321 }
316322}
0 commit comments