Skip to content

Commit 2d3fdda

Browse files
committed
Add IEventListView and improve collection view APIs
Introduced IEventListView for immutable event collections. Renamed DeclaringTypData to DeclaringType for consistency. Added and updated EventListView and ParameterListView classes to provide strongly-typed, immutable views with enhanced equality and hash code support. Improved equality operators for EventList and ParameterList to support comparisons with their view interfaces.
1 parent 484d61e commit 2d3fdda

7 files changed

Lines changed: 239 additions & 17 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ internal EventDataView(SymbolReflectionInfoCacheKey cacheKey) : base(cacheKey)
5555
public ITypeDataView EventHandlerTypeData { get; }
5656
public IMethodDataView EventInvokerMethodData { get; }
5757
public IMethodDataView RemoveMethodData { get; }
58-
public ITypeDataView DeclaringTypData { get; }
58+
public ITypeDataView DeclaringType { get; }
5959
public ITypeDataView ImplementingTypData { get; }
6060

6161
public void AddEventHandler(object eventSource, Delegate handler) => throw new NotImplementedException();

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

Lines changed: 213 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,34 @@ public bool Equals(EventList? other)
126126
return true;
127127
}
128128

129+
public bool Equals(IEventListView? other)
130+
{
131+
if (other is null)
132+
{
133+
return false;
134+
}
135+
136+
if (Count != other.Count)
137+
{
138+
return false;
139+
}
140+
141+
if (!ReferenceEquals(DeclaringTypeData.View, other.DeclaringType))
142+
{
143+
return false;
144+
}
145+
146+
for (int index = 0; index < Count; index++)
147+
{
148+
if (!Events[index].View.Equals(other.Events[index]))
149+
{
150+
return false;
151+
}
152+
}
153+
154+
return true;
155+
}
156+
129157
public override bool Equals(object? obj)
130158
=> obj is EventList other && Equals(other);
131159

@@ -147,8 +175,189 @@ private int ComputeHashCode()
147175
}
148176
}
149177

150-
public static bool operator ==(EventList? left, EventList? right)
151-
=> left?.Equals(right) ?? (right is null);
152-
public static bool operator !=(EventList? left, EventList? right)
153-
=> !(left == right);
178+
public static bool operator ==(EventList? left, EventList? right) => left?.Equals(right) ?? (right is null);
179+
public static bool operator !=(EventList? left, EventList? right) => !(left == right);
180+
181+
public static bool operator ==(EventList? left, IEventListView? right) => left?.Equals(right) ?? (right is null);
182+
public static bool operator !=(EventList? left, IEventListView? right) => !(left == right);
183+
184+
public static bool operator ==(IEventListView? left, EventList? right) => right == left;
185+
public static bool operator !=(IEventListView? left, EventList? right) => !(left == right);
186+
}
187+
188+
public sealed class EventListView : IReadOnlyList<IEventDataView>, IEquatable<IEventListView>, IEventListView
189+
{
190+
public static EventListView Empty { get; } = new EventListView();
191+
private readonly int _hashCode; // precomputed
192+
private readonly ITypeDataView _declaringType;
193+
private readonly Dictionary<string, IEventDataView> _eventNameIndex;
194+
195+
public EventListView(IEventDataView[] items, ITypeDataView? declaringType) : this((IEnumerable<IEventDataView>)items, declaringType)
196+
{
197+
}
198+
199+
public EventListView(IEnumerable<IEventDataView> items, ITypeDataView? declaringType)
200+
{
201+
ArgumentNullException.ThrowIfNull(declaringType);
202+
203+
_declaringType = declaringType;
204+
Events = items?.ToImmutableList() ?? ImmutableList<IEventDataView>.Empty;
205+
_eventNameIndex = Events.ToDictionary(eventData => eventData.Name, StringComparer.Ordinal);
206+
207+
if (HasItems)
208+
{
209+
ArgumentExceptionAdvanced.ThrowIfAny(
210+
Events,
211+
eventData => !ReferenceEquals(eventData.DeclaringType, _declaringType),
212+
nameof(items),
213+
$"At least one item in the argument sequence '{nameof(items)}' has a different value for the '{nameof(IEventDataView)}.{nameof(IEventDataView.DeclaringType)}' declaring type handle. All events must belong to the same declaring type.");
214+
215+
}
216+
217+
_hashCode = ComputeHashCode();
218+
}
219+
220+
internal EventListView(IEnumerable<IEventDataView> items, ITypeDataView? declaringType, bool isIntegrityValidationEnabled)
221+
{
222+
ArgumentNullException.ThrowIfNull(declaringType);
223+
224+
_declaringType = declaringType;
225+
226+
Events = items?.ToImmutableList() ?? ImmutableList<IEventDataView>.Empty;
227+
_eventNameIndex = Events.ToDictionary(eventData => eventData.Name, StringComparer.Ordinal);
228+
229+
if (isIntegrityValidationEnabled && HasItems)
230+
{
231+
ArgumentExceptionAdvanced.ThrowIfAny(
232+
Events,
233+
eventData => !ReferenceEquals(eventData.DeclaringType, _declaringType),
234+
nameof(items),
235+
$"At least one item in the argument sequence '{nameof(items)}' has a different value for the '{nameof(IEventDataView)}.{nameof(IEventDataView.DeclaringType)}' declaring type handle. All events must belong to the same declaring type.");
236+
237+
}
238+
239+
_hashCode = ComputeHashCode();
240+
}
241+
242+
private EventListView()
243+
{
244+
_declaringType = default!;
245+
Events = ImmutableList<IEventDataView>.Empty;
246+
_eventNameIndex = new Dictionary<string, IEventDataView>(0, StringComparer.Ordinal);
247+
_hashCode = ComputeHashCode();
248+
}
249+
250+
public bool TryGetEventByName(string eventName, out IEventDataView? eventData)
251+
{
252+
ArgumentNullException.ThrowIfNullOrWhiteSpace(eventName);
253+
return _eventNameIndex.TryGetValue(eventName, out eventData);
254+
}
255+
256+
public int Count => Events.Count;
257+
public bool IsEmpty => Events.IsEmpty;
258+
public bool HasItems => !IsEmpty;
259+
public ImmutableList<IEventDataView> Events { get; }
260+
public ITypeDataView DeclaringType => _declaringType ?? throw new InvalidOperationException(ExceptionMessages.GetInvalidAccessCollectionEmptyExceptionMessage(nameof(EventListView), nameof(DeclaringType)));
261+
262+
public IEventDataView this[int index]
263+
{
264+
get
265+
{
266+
ArgumentOutOfRangeException.ThrowIfLessThan(index, 0, nameof(index));
267+
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, Events.Count, nameof(index));
268+
269+
return HasItems
270+
? Events[index]
271+
: throw new InvalidOperationException(ExceptionMessages.GetInvalidAccessCollectionEmptyExceptionMessage(nameof(EventList), ReflectionConstants.IndexerGetMethodName));
272+
}
273+
}
274+
275+
public IEnumerator<IEventDataView> GetEnumerator()
276+
=> ((IEnumerable<IEventDataView>)Events).GetEnumerator();
277+
278+
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
279+
=> Events.GetEnumerator();
280+
281+
public bool Equals(IEventListView? other)
282+
{
283+
if (other is null)
284+
{
285+
return false;
286+
}
287+
288+
if (Count != other.Count)
289+
{
290+
return false;
291+
}
292+
293+
if (!ReferenceEquals(DeclaringType, other.DeclaringType))
294+
{
295+
return false;
296+
}
297+
298+
for (int index = 0; index < Count; index++)
299+
{
300+
if (!Events[index].Equals(other.Events[index]))
301+
{
302+
return false;
303+
}
304+
}
305+
306+
return true;
307+
}
308+
309+
internal bool Equals(EventList? other)
310+
{
311+
if (other is null)
312+
{
313+
return false;
314+
}
315+
316+
if (Count != other.Count)
317+
{
318+
return false;
319+
}
320+
321+
if (!ReferenceEquals(DeclaringType, other.DeclaringTypeData.View))
322+
{
323+
return false;
324+
}
325+
326+
for (int index = 0; index < Count; index++)
327+
{
328+
if (!Events[index].Equals(other.Events[index].View))
329+
{
330+
return false;
331+
}
332+
}
333+
334+
return true;
335+
}
336+
337+
public override bool Equals(object? obj)
338+
=> obj is IEventListView other && Equals(other);
339+
340+
public override int GetHashCode() => _hashCode;
341+
342+
private int ComputeHashCode()
343+
{
344+
unchecked
345+
{
346+
var hashCode = new HashCode();
347+
hashCode.Add(Count);
348+
hashCode.Add(DeclaringType);
349+
for (int index = 0; index < Events.Count; index++)
350+
{
351+
hashCode.Add(Events[index]);
352+
}
353+
354+
return hashCode.ToHashCode();
355+
}
356+
}
357+
358+
public static bool operator ==(EventListView? left, IEventListView? right) => left?.Equals(right) ?? (right is null);
359+
public static bool operator !=(EventListView? left, IEventListView? right) => !(left == right);
360+
361+
public static bool operator ==(IEventListView? left, EventListView? right) => left?.Equals(right) ?? (right is null);
362+
public static bool operator !=(IEventListView? left, EventListView? right) => !(left == right);
154363
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace BionicCode.Utilities.Net.Reflection;
2+
3+
using System.Collections.Immutable;
4+
5+
public interface IEventListView : IEquatable<IEventListView>
6+
{
7+
IEventDataView this[int index] { get; }
8+
int Count { get; }
9+
ITypeDataView DeclaringType { get; }
10+
ImmutableList<IEventDataView> Events { get; }
11+
bool HasItems { get; }
12+
bool IsEmpty { get; }
13+
IEnumerator<IEventDataView> GetEnumerator();
14+
bool TryGetEventByName(string eventName, out IEventDataView? eventData);
15+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public interface IMemberDataView : ISymbolInfoDataView
88
{
99
AccessModifier AccessModifier { get; }
1010
BindingFlags BindingFlagsVisibilityMask { get; }
11-
ITypeDataView DeclaringTypData { get; }
11+
ITypeDataView DeclaringType { get; }
1212
RuntimeTypeHandle DeclaringTypeHandle { get; }
1313
RuntimeTypeHandle ImplementingTypeHandle { get; }
1414
ITypeDataView ImplementingTypData { get; }

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@
22

33
using System.Collections.Immutable;
44

5-
public interface IParameterListView
5+
public interface IParameterListView : IEquatable<IParameterListView>
66
{
77
IParameterDataView this[int index] { get; }
88
int Count { get; }
99
IParameterizedMemberDataView DeclaringMemberDataView { get; }
1010
bool HasItems { get; }
1111
bool IsEmpty { get; }
1212
ImmutableList<IParameterDataView> Parameters { get; }
13-
bool Equals(object? obj);
14-
bool Equals(IParameterListView? other);
1513
IEnumerator<IParameterDataView> GetEnumerator();
16-
int GetHashCode();
1714
bool TryGetParameterByName(string parameterName, out IParameterDataView? parameterData);
1815
}

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,15 @@ private int ComputeHashCode()
206206
=> left?.Equals(right) ?? (right is null);
207207
public static bool operator !=(ParameterList? left, ParameterList? right)
208208
=> !(left == right);
209-
public static bool operator ==(ParameterList left, IParameterListView? right) => left?.Equals(right) ?? (right is null);
210-
public static bool operator !=(ParameterList left, IParameterListView? right) => !(left == right);
209+
public static bool operator ==(ParameterList? left, IParameterListView? right) => left?.Equals(right) ?? (right is null);
210+
public static bool operator !=(ParameterList? left, IParameterListView? right) => !(left == right);
211+
212+
public static bool operator ==(IParameterListView? left, ParameterList? right) => right == left;
213+
public static bool operator !=(IParameterListView? left, ParameterList? right) => !(left == right);
211214
}
212215
#endregion ParameterList
213216

214-
internal sealed class ParameterListView : IReadOnlyList<IParameterDataView>, IEquatable<IParameterListView>, IParameterListView
217+
public sealed class ParameterListView : IReadOnlyList<IParameterDataView>, IEquatable<IParameterListView>, IParameterListView
215218
{
216219
public static IParameterListView Empty { get; } = new ParameterListView();
217220
private readonly int _hashCode; // precomputed
@@ -347,7 +350,7 @@ public bool Equals(IParameterListView? other)
347350
return true;
348351
}
349352

350-
public bool Equals(ParameterList? other)
353+
internal bool Equals(ParameterList? other)
351354
{
352355
if (other is null)
353356
{
@@ -397,8 +400,6 @@ private int ComputeHashCode()
397400
}
398401
}
399402

400-
public static bool operator ==(ParameterListView? left, ParameterList? right) => left?.Equals(right) ?? (right is null);
401-
public static bool operator !=(ParameterListView? left, ParameterList? right) => !(left == right);
402403
public static bool operator ==(IParameterListView? left, ParameterListView? right) => left?.Equals(right) ?? (right is null);
403404
public static bool operator !=(IParameterListView? left, ParameterListView? right) => !(left == right);
404405
public static bool operator ==(ParameterListView? left, IParameterListView? right) => left?.Equals(right) ?? (right is null);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ public bool TryGetPropertySetMethodData(out IMethodDataView? setMethodData)
396396
public ITypeDataView? PropertyTypeData { get; }
397397
public AccessModifier AccessModifier { get; }
398398
public BindingFlags BindingFlagsVisibilityMask { get; }
399-
public ITypeDataView? DeclaringTypData { get; }
399+
public ITypeDataView? DeclaringType { get; }
400400
public RuntimeTypeHandle DeclaringTypeHandle { get; }
401401
public RuntimeTypeHandle ImplementingTypeHandle { get; }
402402
public ITypeDataView? ImplementingTypData { get; }

0 commit comments

Comments
 (0)