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
+ }
+ }
}
}