diff --git a/src/CloudNative.CloudEvents/CloudEventAttribute.cs b/src/CloudNative.CloudEvents/CloudEventAttribute.cs index 15a6573..1e0e5dc 100644 --- a/src/CloudNative.CloudEvents/CloudEventAttribute.cs +++ b/src/CloudNative.CloudEvents/CloudEventAttribute.cs @@ -176,5 +176,97 @@ public object Validate(object value) } return value; } + + /// + /// Returns an equality comparer for values that just uses the attribute name + /// for equality comparisons. Validators, types and kinds (optional, required, extension) are not included in the comparison. + /// + public static IEqualityComparer NameComparer { get; } = new NameComparerImpl(); + + /// + /// Returns an equality comparer for values that just uses the attribute name + /// and type for equality comparisons. Validators and kinds (optional, required, extension) are not included in the comparison. + /// + public static IEqualityComparer NameTypeComparer { get; } = new NameTypeComparerImpl(); + + /// + /// Returns an equality comparer for values that uses the attribute name, + /// type and kind (optional, required, extension) for equality comparisons. Validators are not included in the comparison. + /// + public static IEqualityComparer NameTypeKindComparer { get; } = new NameTypeKindComparerImpl(); + + /// + /// Base class for all comparers, just to avoid having to worry about nullity in every implementation. + /// + private abstract class ComparerBase : IEqualityComparer + { + public bool Equals(CloudEventAttribute x, CloudEventAttribute y) => + (x is null && y is null) || + (x is not null && y is not null && EqualsImpl(x, y)); + + public int GetHashCode(CloudEventAttribute obj) + { + Validation.CheckNotNull(obj, nameof(obj)); + return GetHashCodeImpl(obj); + } + + protected abstract bool EqualsImpl(CloudEventAttribute x, CloudEventAttribute y); + protected abstract int GetHashCodeImpl(CloudEventAttribute obj); + } + + private sealed class NameComparerImpl : ComparerBase + { + protected override bool EqualsImpl(CloudEventAttribute x, CloudEventAttribute y) => x.Name == y.Name; + + protected override int GetHashCodeImpl(CloudEventAttribute obj) => obj.Name.GetHashCode(); + } + + private sealed class NameTypeComparerImpl : ComparerBase + { + protected override bool EqualsImpl(CloudEventAttribute x, CloudEventAttribute y) => + x.Name == y.Name && + x.Type == y.Type; + + protected override int GetHashCodeImpl(CloudEventAttribute obj) + { +#if NETSTANDARD2_1_OR_GREATER + return HashCode.Combine(obj.Name, obj.Type); +#else + unchecked + { + int hash = 19; + hash = hash * 31 + obj.Name.GetHashCode(); + hash = hash * 31 + obj.Type.GetHashCode(); + return hash; + } +#endif + } + } + + private sealed class NameTypeKindComparerImpl : ComparerBase + { + protected override bool EqualsImpl(CloudEventAttribute x, CloudEventAttribute y) => + x.Name == y.Name && + x.Type == y.Type && + x.IsExtension == y.IsExtension && + x.IsRequired == y.IsRequired; + + protected override int GetHashCodeImpl(CloudEventAttribute obj) + { +#if NETSTANDARD2_1_OR_GREATER + return HashCode.Combine(obj.Name, obj.Type, obj.IsExtension, obj.IsRequired); +#else + unchecked + { + int hash = 19; + hash = hash * 31 + obj.Name.GetHashCode(); + hash = hash * 31 + obj.Type.GetHashCode(); + hash = hash * 31 + obj.IsExtension.GetHashCode(); + hash = hash * 31 + obj.IsRequired.GetHashCode(); + return hash; + } +#endif + } + } } }