Skip to content

Commit b955779

Browse files
committed
Add symbol list view interfaces and refactor list classes
Introduced IConstructorListView, IFieldListView, IPropertyListView, and ITypeListView interfaces for strongly-typed, immutable access to symbol data views. Added ISymbolViewProvider<TSymbolView> for exposing View properties. Refactored list and builder classes to enforce declaring type consistency, updated equality and indexer logic to use reference equality and views, and improved error handling. Implemented new list view classes and enhanced querying methods for improved type safety and usability.
1 parent 2d3fdda commit b955779

27 files changed

Lines changed: 1072 additions & 296 deletions

‎src/BionicCode.Utilities.Reflection/ConstructorData.cs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ internal ConstructorData(WellKnownConstructorDescriptor descriptor)
4747
IsExplicitInterfaceImplementation = false;
4848
}
4949

50-
internal new IConstructorDataView View => _constructorDataView
51-
??= new ConstructorDataView(GetPublicCacheKey());
50+
internal new IConstructorDataView View => _constructorDataView ??= new ConstructorDataView(GetPublicCacheKey());
5251

5352
internal ConstructorInfo ConstructorInfo { get; }
5453

‎src/BionicCode.Utilities.Reflection/ConstructorList.cs‎

Lines changed: 200 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public bool Equals(ConstructorList? other)
104104

105105
for (int index = 0; index < Count; index++)
106106
{
107-
if (!Constructors[index].Equals(other.Constructors[index]))
107+
if (!ReferenceEquals(Constructors[index], other.Constructors[index]))
108108
{
109109
return false;
110110
}
@@ -113,11 +113,37 @@ public bool Equals(ConstructorList? other)
113113
return true;
114114
}
115115

116-
public override bool Equals(object? obj)
117-
=> obj is ConstructorList other && Equals(other);
116+
public bool Equals(IConstructorListView? other)
117+
{
118+
if (other is null)
119+
{
120+
return false;
121+
}
122+
123+
if (Count != other.Count)
124+
{
125+
return false;
126+
}
118127

119-
public override int GetHashCode()
120-
=> _hashCode;
128+
if (!ReferenceEquals(DeclaringTypeData.View, other.DeclaringType))
129+
{
130+
return false;
131+
}
132+
133+
for (int index = 0; index < Count; index++)
134+
{
135+
if (!Constructors[index].View.Equals(other.Constructors[index]))
136+
{
137+
return false;
138+
}
139+
}
140+
141+
return true;
142+
}
143+
144+
public override bool Equals(object? obj) => obj is ConstructorList other && Equals(other);
145+
146+
public override int GetHashCode() => _hashCode;
121147

122148
private int ComputeHashCode()
123149
{
@@ -135,8 +161,173 @@ private int ComputeHashCode()
135161
}
136162
}
137163

138-
public static bool operator ==(ConstructorList? left, ConstructorList? right)
139-
=> left?.Equals(right) ?? (right is null);
140-
public static bool operator !=(ConstructorList? left, ConstructorList? right)
141-
=> !(left == right);
164+
public static bool operator ==(ConstructorList? left, ConstructorList? right) => left?.Equals(right) ?? (right is null);
165+
public static bool operator !=(ConstructorList? left, ConstructorList? right) => !(left == right);
166+
167+
public static bool operator ==(IConstructorListView? left, ConstructorList? right) => right == left;
168+
public static bool operator !=(IConstructorListView? left, ConstructorList? right) => !(left == right);
169+
170+
public static bool operator ==(ConstructorList? left, IConstructorListView? right) => left?.Equals(right) ?? (right is null);
171+
public static bool operator !=(ConstructorList? left, IConstructorListView? right) => !(left == right);
172+
}
173+
174+
public sealed class ConstructorListView : IReadOnlyList<IConstructorDataView>, IEquatable<IConstructorListView>, IConstructorListView
175+
{
176+
public static IConstructorListView Empty { get; } = new ConstructorListView();
177+
private readonly int _hashCode; // precomputed
178+
private readonly ITypeDataView _declaringType;
179+
public ConstructorListView(IConstructorDataView[] items, ITypeDataView? declaringType) : this((IEnumerable<IConstructorDataView>)items, declaringType)
180+
{
181+
}
182+
183+
public ConstructorListView(IEnumerable<IConstructorDataView> items, ITypeDataView? declaringType)
184+
{
185+
ArgumentNullExceptionAdvanced.ThrowIfNull(declaringType);
186+
187+
_declaringType = declaringType;
188+
Constructors = items?.ToImmutableList() ?? ImmutableList<IConstructorDataView>.Empty;
189+
if (HasItems)
190+
{
191+
192+
ArgumentExceptionAdvanced.ThrowIfAny(
193+
Constructors,
194+
constructorData => !ReferenceEquals(constructorData.DeclaringType, _declaringType),
195+
nameof(items),
196+
$"At least one item in the argument sequence '{nameof(items)}' has a different value for the '{nameof(IConstructorDataView.DeclaringType)}' declaring type handle. All constructors must belong to the same declaring type.");
197+
}
198+
199+
_hashCode = ComputeHashCode();
200+
}
201+
202+
internal ConstructorListView(IEnumerable<IConstructorDataView> items, ITypeDataView? declaringType, bool isIntegrityValidationEnabled)
203+
{
204+
ArgumentNullExceptionAdvanced.ThrowIfNull(declaringType);
205+
206+
_declaringType = declaringType;
207+
Constructors = items?.ToImmutableList() ?? ImmutableList<IConstructorDataView>.Empty;
208+
if (isIntegrityValidationEnabled && HasItems)
209+
{
210+
ArgumentExceptionAdvanced.ThrowIfAny(
211+
Constructors,
212+
constructorData => !ReferenceEquals(constructorData.DeclaringType, _declaringType),
213+
nameof(items),
214+
$"At least one item in the argument sequence '{nameof(items)}' has a different value for the '{nameof(IConstructorDataView.DeclaringType)}' declaring type handle. All constructors must belong to the same declaring type.");
215+
}
216+
217+
_hashCode = ComputeHashCode();
218+
}
219+
220+
private ConstructorListView()
221+
{
222+
Constructors = ImmutableList<IConstructorDataView>.Empty;
223+
_declaringType = default!;
224+
_hashCode = ComputeHashCode();
225+
}
226+
227+
public int Count => Constructors.Count;
228+
public bool IsEmpty => Constructors.IsEmpty;
229+
public bool HasItems => !IsEmpty;
230+
public ImmutableList<IConstructorDataView> Constructors { get; }
231+
public ITypeDataView DeclaringType => _declaringType ?? throw new InvalidOperationException(ExceptionMessages.GetInvalidAccessCollectionEmptyExceptionMessage(GetType().Name, nameof(DeclaringType)));
232+
233+
public IConstructorDataView this[int index]
234+
{
235+
get
236+
{
237+
ArgumentOutOfRangeException.ThrowIfLessThan(index, 0, nameof(index));
238+
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, Constructors.Count, nameof(index));
239+
240+
return HasItems
241+
? Constructors[index]
242+
: throw new InvalidOperationException(ExceptionMessages.GetInvalidAccessCollectionEmptyExceptionMessage(nameof(ConstructorListView), ReflectionConstants.IndexerGetMethodName));
243+
}
244+
}
245+
246+
public IEnumerator<IConstructorDataView> GetEnumerator()
247+
=> ((IEnumerable<IConstructorDataView>)Constructors).GetEnumerator();
248+
249+
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
250+
=> Constructors.GetEnumerator();
251+
252+
public bool Equals(IConstructorListView? other)
253+
{
254+
if (other is null)
255+
{
256+
return false;
257+
}
258+
259+
if (Count != other.Count)
260+
{
261+
return false;
262+
}
263+
264+
if (!ReferenceEquals(DeclaringType, other.DeclaringType))
265+
{
266+
return false;
267+
}
268+
269+
for (int index = 0; index < Count; index++)
270+
{
271+
if (!Constructors[index].Equals(other.Constructors[index]))
272+
{
273+
return false;
274+
}
275+
}
276+
277+
return true;
278+
}
279+
280+
internal bool Equals(ConstructorList? other)
281+
{
282+
if (other is null)
283+
{
284+
return false;
285+
}
286+
287+
if (Count != other.Count)
288+
{
289+
return false;
290+
}
291+
292+
if (!ReferenceEquals(DeclaringType, other.DeclaringTypeData.View))
293+
{
294+
return false;
295+
}
296+
297+
for (int index = 0; index < Count; index++)
298+
{
299+
if (!Constructors[index].Equals(other.Constructors[index].View))
300+
{
301+
return false;
302+
}
303+
}
304+
305+
return true;
306+
}
307+
308+
public override bool Equals(object? obj) => obj is ConstructorListView other && Equals(other);
309+
310+
public override int GetHashCode() => _hashCode;
311+
312+
private int ComputeHashCode()
313+
{
314+
unchecked
315+
{
316+
var hashCode = new HashCode();
317+
hashCode.Add(Count);
318+
hashCode.Add(DeclaringType);
319+
for (int index = 0; index < Constructors.Count; index++)
320+
{
321+
hashCode.Add(Constructors[index]);
322+
}
323+
324+
return hashCode.ToHashCode();
325+
}
326+
}
327+
328+
public static bool operator ==(ConstructorListView? left, IConstructorListView? right) => left?.Equals(right) ?? (right is null);
329+
public static bool operator !=(ConstructorListView? left, IConstructorListView? right) => !(left == right);
330+
331+
public static bool operator ==(IConstructorListView? left, ConstructorListView? right) => left?.Equals(right) ?? (right is null);
332+
public static bool operator !=(IConstructorListView? left, ConstructorListView? right) => !(left == right);
142333
}

‎src/BionicCode.Utilities.Reflection/EventData.cs‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,9 @@ internal EventData(WellKnownEventDescriptor descriptor)
4949
EventInfo = _descriptor.EventInfo;
5050
}
5151

52-
internal IEventDataView View => _eventDataView
53-
??= new EventDataView(GetPublicCacheKey());
52+
internal new IEventDataView View => _eventDataView ??= new EventDataView(GetPublicCacheKey());
5453

55-
protected override MemberInfo MemberInfo
56-
=> EventInfo;
54+
protected override MemberInfo MemberInfo => EventInfo;
5755

5856
internal object? RaiseEvent(object? target, params object?[]? arguments)
5957
{

‎src/BionicCode.Utilities.Reflection/EventList.cs‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private EventList()
6363
{
6464
_declaringType = default!;
6565
Events = ImmutableList<EventData>.Empty;
66-
_eventNameIndex = new Dictionary<string, EventData>(0, StringComparer.Ordinal);
66+
_eventNameIndex = [];
6767
_hashCode = ComputeHashCode();
6868
}
6969

@@ -73,6 +73,12 @@ public bool TryGetEventByName(string eventName, out EventData? eventData)
7373
return _eventNameIndex.TryGetValue(eventName, out eventData);
7474
}
7575

76+
public bool ContainsEventWithName(string eventName)
77+
{
78+
ArgumentNullException.ThrowIfNullOrWhiteSpace(eventName);
79+
return _eventNameIndex.ContainsKey(eventName);
80+
}
81+
7682
public int Count => Events.Count;
7783
public bool IsEmpty => Events.IsEmpty;
7884
public bool HasItems => !IsEmpty;
@@ -117,7 +123,7 @@ public bool Equals(EventList? other)
117123

118124
for (int index = 0; index < Count; index++)
119125
{
120-
if (!Events[index].Equals(other.Events[index]))
126+
if (!ReferenceEquals(Events[index], other.Events[index]))
121127
{
122128
return false;
123129
}
@@ -187,7 +193,7 @@ private int ComputeHashCode()
187193

188194
public sealed class EventListView : IReadOnlyList<IEventDataView>, IEquatable<IEventListView>, IEventListView
189195
{
190-
public static EventListView Empty { get; } = new EventListView();
196+
public static IEventListView Empty { get; } = new EventListView();
191197
private readonly int _hashCode; // precomputed
192198
private readonly ITypeDataView _declaringType;
193199
private readonly Dictionary<string, IEventDataView> _eventNameIndex;
@@ -243,7 +249,7 @@ private EventListView()
243249
{
244250
_declaringType = default!;
245251
Events = ImmutableList<IEventDataView>.Empty;
246-
_eventNameIndex = new Dictionary<string, IEventDataView>(0, StringComparer.Ordinal);
252+
_eventNameIndex = [];
247253
_hashCode = ComputeHashCode();
248254
}
249255

@@ -253,6 +259,12 @@ public bool TryGetEventByName(string eventName, out IEventDataView? eventData)
253259
return _eventNameIndex.TryGetValue(eventName, out eventData);
254260
}
255261

262+
public bool ContainsEventWithName(string eventName)
263+
{
264+
ArgumentNullException.ThrowIfNullOrWhiteSpace(eventName);
265+
return _eventNameIndex.ContainsKey(eventName);
266+
}
267+
256268
public int Count => Events.Count;
257269
public bool IsEmpty => Events.IsEmpty;
258270
public bool HasItems => !IsEmpty;
@@ -268,7 +280,7 @@ public IEventDataView this[int index]
268280

269281
return HasItems
270282
? Events[index]
271-
: throw new InvalidOperationException(ExceptionMessages.GetInvalidAccessCollectionEmptyExceptionMessage(nameof(EventList), ReflectionConstants.IndexerGetMethodName));
283+
: throw new InvalidOperationException(ExceptionMessages.GetInvalidAccessCollectionEmptyExceptionMessage(nameof(EventListView), ReflectionConstants.IndexerGetMethodName));
272284
}
273285
}
274286

‎src/BionicCode.Utilities.Reflection/EventListBuilder.cs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public static EventList Create(IEnumerable<EventInfo>? items)
5151
events.Add(eventData);
5252
}
5353

54+
if (declaringType is null)
55+
{
56+
throw new ArgumentException($"The argument '{nameof(items)}' contains invalid items. Reason: Unable to determine the declaring type of the provided '{nameof(EventInfo)}' items.");
57+
}
58+
5459
return events.ToEventList(declaringType);
5560
}
5661

@@ -83,7 +88,7 @@ EventList IEventListBuilder.Build()
8388

8489
internal static class EventListBuilderExtensions
8590
{
86-
public static EventList ToEventList(this IEnumerable<EventData> items, TypeData? declaringType)
91+
public static EventList ToEventList(this IEnumerable<EventData> items, TypeData declaringType)
8792
=> items is null || items.IsEmpty() ? EventList.Empty : new EventList(items, declaringType);
8893

8994
/// <summary>

‎src/BionicCode.Utilities.Reflection/FieldData.cs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ internal FieldData(WellKnownFieldDescriptor descriptor)
5151
IsExplicitInterfaceImplementation = false; // Fields cannot be explicit interface implementations
5252
ImplementingTypeHandle = DeclaringTypeHandle;
5353
}
54-
internal new IFieldDataView View => _fieldDataView
55-
??= new FieldDataView(GetPublicCacheKey());
54+
internal new IFieldDataView View => _fieldDataView ??= new FieldDataView(GetPublicCacheKey());
5655

5756
internal FieldInfo FieldInfo { get; }
5857

0 commit comments

Comments
 (0)