diff --git a/README.md b/README.md index dab7506..1ab0530 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ It was built for C# 12 and .NET 8. It checks `record class` and `record struct` - Otherwise all members are checked for: - the member is a primitive type, enum or string (these are ok) - it is a object or dynamic (these are never ok) - - it is an inline array (these are never ok) - new in version 1.2 for .NET 8 + - it is an inline array (these are never ok) - **new in version 1.2 for .NET 8** - it has Equals(T) or Equals(object) method overriden directly in the type (these are ok) - it is a record (these will be checked elsewhere, so are assumed ok here) - it is a class (without Equals method, these are not ok) diff --git a/RecordValueAnalyser.Package/README.md b/RecordValueAnalyser.Package/README.md index f340074..65ecdc1 100644 --- a/RecordValueAnalyser.Package/README.md +++ b/RecordValueAnalyser.Package/README.md @@ -75,7 +75,7 @@ It was built for C# 12 and .NET 8. It checks `record class` and `record struct` - Otherwise all members are checked for: - the member is a primitive type, enum or string (these are ok) - it is a object or dynamic (these are never ok) - - it is an inline array (these are never ok) - new in version 1.2 for .NET 8 + - it is an inline array (these are never ok) - **new in version 1.2 for .NET 8** - it has Equals(T) or Equals(object) method overriden directly in the type (these are ok) - it is a record (these will be checked elsewhere, so are assumed ok here) - it is a class (without Equals method, these are not ok) @@ -110,99 +110,3 @@ public record struct Test(IReadOnlyList Numbers) ``` It is not necessary for records to implement `IEquatable`. When you write your implementations `SequenceEqual` is very useful for comparing collections. - -Note that GetHashCode for collections is tricky! - -``` -public override int GetHashCode() => Numbers.GetHashCode(); // BROKEN! -public override int GetHashCode() => HashCode.Combine(Numbers); // BROKEN! - -public override int GetHashCode() // CORRECT IMPLEMENTATION -{ - var hash = new HashCode(); - foreach (var n in Numbers) hash.Add(n); - return hash.ToHashCode(); -} - -public readonly bool Equals(Test other) => Numbers.SequenceEqual(other.Numbers); // CORRECT IMPLEMENTATION -``` - -## Examples - -These are taken from the test project. Members expected to pass are named `Pass`, and those expected to fail are named `Fail`. - -``` -// these check members, called parameters in Roslyn - -public record class A(F FooFail, G BarFail, string SPass, StructA SaFail); - -public record class B(int IPass, IReadOnlyList JFail, int? NullableIntPass, StructB BaPass); - -public record struct AS(F FooFail, G GShouldFail, H HShouldPass, string SPass, Inner InnPass, object OFail); - -public record struct Tup1(int IPass, (int a, int b) TupPass, DateTime? DtPass); - -public record struct Tup2(int IPass, (int a, int[] b, object o) TupFail); - -public record struct Tup3(int IPass, (bool, int) TupPass); - -// this checks fields and properties -public record class RecFields(int IPass, string SPass, object OFail) -{ - public IList? FieldFail; - public int[]? PropertyFail { get; set; } - - public int FieldPass; - public string? PropertyPass { get; set; } - - //uncomment, and the failures will disappear - //public virtual bool Equals(RecFields? other) => true; - - public override int GetHashCode() => 0; // TODO -} - -// the record class has an Equals method, so its assumed to be ok -public record class HasEqualsRecordClass(IReadOnlyList NumsPass) -{ - public virtual bool Equals(HasEqualsRecordClass? other) => other != null && NumsPass.SequenceEqual(other.NumsPass); - - public override int GetHashCode() => 0; // TODO -} - -// the record struct has an Equals method, so its assumed to be ok -public record struct HasEqualsRecordStruct(IReadOnlyList NumsPass) -{ - public readonly bool Equals(HasEqualsRecordStruct other) => NumsPass.SequenceEqual(other.NumsPass); - - public override readonly int GetHashCode() => 0; // TODO -} - -// ============= Supporting types ============= - -// this is nested inside another record -public record Inner(int IPass, string JPass, DateTime DtPass); - -// when used in record, this fails because it is a class with no Equals method -public class F { public int[]? n; } - -// when used in record, this fails because it is a class with no Equals method -public class G { public int i; } - -// when used in record, this passes because it has Equals(T) -public class H -{ - public int i; - - public bool Equals(H? other) => other != null && other.i == this.i; - - //public override bool Equals(object? obj) => Equals(obj as H); - - public override int GetHashCode() => 0; // TODO -} - -// when used in a record, this fails because no Equals method -public struct StructA { public int[] Numbers; } - -// when used in a record, this passes because its fields have value semantics -public struct StructB { public int A; public string S; } -```