Added support for collections & dictionaries
- SortedCollectionEqualityComparer - UnsortedCollectionEqualityComparer - DictionaryEqualityComparer
This commit is contained in:
Родитель
c677a034c1
Коммит
4d981ed50d
|
@ -16,6 +16,7 @@
|
|||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using FluentAssertions;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
@ -26,7 +27,7 @@ namespace Uno.CodeGen.Tests
|
|||
public class Given_GeneratedEquality
|
||||
{
|
||||
[TestMethod]
|
||||
public void EqualityWithCustomComparer()
|
||||
public void Equality_WhenUsingCustomComparer()
|
||||
{
|
||||
var e1 = GeneratedImmutableEntityForEquality.Default.WithId("a");
|
||||
var e2 = GeneratedImmutableEntityForEquality.Default.WithId("A");
|
||||
|
@ -45,6 +46,137 @@ namespace Uno.CodeGen.Tests
|
|||
typeof(GeneratedImmutableEntityForEquality).Should()
|
||||
.HaveImplictConversionOperator<GeneratedImmutableEntityForEquality, GeneratedImmutableEntityForEquality.Builder>();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equality_WhenUsingArray()
|
||||
{
|
||||
var e1 = new MyEntityForArrayAndDictionaryEquals.Builder {Array = new[] {"a", "b", "c"}}.ToImmutable();
|
||||
var e2 = new MyEntityForArrayAndDictionaryEquals.Builder {Array = new[] {"a", "b", "c"}}.ToImmutable();
|
||||
var e3 = new MyEntityForArrayAndDictionaryEquals.Builder {Array = new[] {"a", "b", "c", "d"}}.ToImmutable();
|
||||
|
||||
e1.Should().NotBeNull();
|
||||
e2.Should().NotBeNull();
|
||||
e3.Should().NotBeNull();
|
||||
|
||||
(e1 == e2).Should().BeTrue();
|
||||
(e1 == e3).Should().BeFalse();
|
||||
e1.Equals(e2).Should().BeTrue();
|
||||
e1.Equals(e3).Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equality_WhenUsingCollectionsAndDictionaries()
|
||||
{
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes()
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes()).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(arraySorted: new[] {"a", "b"})
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(arraySorted: new[] {"a", "b"})).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(arraySorted: new[] { "a", "b" })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(arraySorted: new[] { "b", "a" })).Should().BeFalse();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(arrayUnsorted: new[] { "a", "b" })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(arrayUnsorted: new[] { "a", "b" })).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(arrayUnsorted: new[] { "a", "b" })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(arrayUnsorted: new[] { "b", "a" })).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(listSorted: new List<string>{"a", "b"})
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(listSorted: new List<string> { "a", "b" })).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(listSorted: new List<string> { "a", "b" })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(listSorted: new List<string> { "b", "a" })).Should().BeFalse();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(listUnsorted: new List<string> { "a", "b" })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(listUnsorted: new List<string> { "a", "b" })).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(listUnsorted: new List<string> { "a", "b" })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(listUnsorted: new List<string> { "b", "a" })).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(readonlyCollectionSorted: ImmutableList.Create("a", "b"))
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(readonlyCollectionSorted: ImmutableList.Create("a", "b"))).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(readonlyCollectionSorted: ImmutableList.Create("a", "b"))
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(readonlyCollectionSorted: ImmutableList.Create("b", "a"))).Should().BeFalse();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(readonlyCollectionUnsorted: ImmutableList.Create("a", "b"))
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(readonlyCollectionUnsorted: ImmutableList.Create("a", "b"))).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(readonlyCollectionUnsorted: ImmutableList.Create("a", "b"))
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(readonlyCollectionUnsorted: ImmutableList.Create("b", "a"))).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(dictionary: new Dictionary<string, string>{{"a", "a"}, {"b", "b"}})
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(dictionary: new Dictionary<string, string> { { "a", "a" }, { "b", "b" } })).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(dictionary: new Dictionary<string, string> { { "a", "a" }, { "b", "b" } })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(dictionary: new Dictionary<string, string> { { "b", "b" }, { "a", "a" } })).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(dictionary: new Dictionary<string, string> { { "a", "a" }, { "b", "b" } })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(dictionary: new Dictionary<string, string> { { "a", "a" }, { "b", "z" } })).Should().BeFalse();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(dictionary: new Dictionary<string, string> { { "a", "a" }, { "b", "b" } })
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(dictionary: new Dictionary<string, string> { { "a", "a" }, { "c", "c" } })).Should().BeFalse();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(readonlyDictionary: ImmutableDictionary<string, string>.Empty.Add("a", "a").Add("b", "b"))
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(readonlyDictionary: ImmutableDictionary<string, string>.Empty.Add("a", "a").Add("b", "b"))).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(readonlyDictionary: ImmutableDictionary<string, string>.Empty.Add("a", "a").Add("b", "b"))
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(readonlyDictionary: ImmutableDictionary<string, string>.Empty.Add("b", "b").Add("a", "a"))).Should().BeTrue();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(readonlyDictionary: ImmutableDictionary<string, string>.Empty.Add("a", "a").Add("b", "b"))
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(readonlyDictionary: ImmutableDictionary<string, string>.Empty.Add("a", "a").Add("b", "z"))).Should().BeFalse();
|
||||
|
||||
new MyEntityForAllCollectionsAndDictionaryTypes(readonlyDictionary: ImmutableDictionary<string, string>.Empty.Add("a", "a").Add("b", "b"))
|
||||
.Equals(new MyEntityForAllCollectionsAndDictionaryTypes(readonlyDictionary: ImmutableDictionary<string, string>.Empty.Add("a", "a").Add("c", "c"))).Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Equality_WhenUsingMyEntityForStringComparison()
|
||||
{
|
||||
new MyEntityForStringComparison.Builder {DefaultMode = "a"}.ToImmutable()
|
||||
.Equals(new MyEntityForStringComparison.Builder {DefaultMode = "a"}.ToImmutable()).Should().BeTrue();
|
||||
|
||||
new MyEntityForStringComparison.Builder { DefaultMode = "a" }.ToImmutable()
|
||||
.Equals(new MyEntityForStringComparison.Builder { DefaultMode = "A" }.ToImmutable()).Should().BeFalse();
|
||||
|
||||
new MyEntityForStringComparison.Builder { DefaultMode = "" }.ToImmutable()
|
||||
.Equals(MyEntityForStringComparison.Default).Should().BeFalse();
|
||||
|
||||
new MyEntityForStringComparison.Builder { IgnoreCase = "a" }.ToImmutable()
|
||||
.Equals(new MyEntityForStringComparison.Builder { IgnoreCase = "a" }.ToImmutable()).Should().BeTrue();
|
||||
|
||||
new MyEntityForStringComparison.Builder { IgnoreCase = "a" }.ToImmutable()
|
||||
.Equals(new MyEntityForStringComparison.Builder { IgnoreCase = "A" }.ToImmutable()).Should().BeTrue();
|
||||
|
||||
new MyEntityForStringComparison.Builder { IgnoreCase = "" }.ToImmutable()
|
||||
.Equals(MyEntityForStringComparison.Default).Should().BeFalse();
|
||||
|
||||
new MyEntityForStringComparison.Builder { EmptyEqualsNull = "a" }.ToImmutable()
|
||||
.Equals(new MyEntityForStringComparison.Builder { EmptyEqualsNull = "a" }.ToImmutable()).Should().BeTrue();
|
||||
|
||||
new MyEntityForStringComparison.Builder { EmptyEqualsNull = "a" }.ToImmutable()
|
||||
.Equals(new MyEntityForStringComparison.Builder { EmptyEqualsNull = "A" }.ToImmutable()).Should().BeFalse();
|
||||
|
||||
new MyEntityForStringComparison.Builder { EmptyEqualsNull = "" }.ToImmutable()
|
||||
.Equals(MyEntityForStringComparison.Default).Should().BeTrue();
|
||||
|
||||
new MyEntityForStringComparison.Builder {EmptyEqualsNull = ""}.ToImmutable()
|
||||
.Should().BeSameAs(MyEntityForStringComparison.Default);
|
||||
|
||||
new MyEntityForStringComparison.Builder { EmptyEqualsNullIgnoreCase = "a" }.ToImmutable()
|
||||
.Equals(new MyEntityForStringComparison.Builder { EmptyEqualsNullIgnoreCase = "a" }.ToImmutable()).Should().BeTrue();
|
||||
|
||||
new MyEntityForStringComparison.Builder { EmptyEqualsNullIgnoreCase = "a" }.ToImmutable()
|
||||
.Equals(new MyEntityForStringComparison.Builder { EmptyEqualsNullIgnoreCase = "A" }.ToImmutable()).Should().BeTrue();
|
||||
|
||||
new MyEntityForStringComparison.Builder { EmptyEqualsNullIgnoreCase = "" }.ToImmutable()
|
||||
.Equals(MyEntityForStringComparison.Default).Should().BeTrue();
|
||||
|
||||
new MyEntityForStringComparison.Builder { EmptyEqualsNullIgnoreCase = "" }.ToImmutable()
|
||||
.Should().BeSameAs(MyEntityForStringComparison.Default);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedEquality]
|
||||
|
@ -104,4 +236,83 @@ namespace Uno.CodeGen.Tests
|
|||
private static IEqualityComparer<string> Id_CustomComparer => StringComparer.OrdinalIgnoreCase;
|
||||
}
|
||||
|
||||
[GeneratedImmutable(GenerateEquality = true)]
|
||||
public partial class MyEntityForArrayAndDictionaryEquals
|
||||
{
|
||||
[EqualityComparerOptions(CollectionMode = CollectionComparerMode.Unsorted)]
|
||||
public string[] Array { get; } = { };
|
||||
|
||||
public ImmutableDictionary<string, string> Dictionary { get; } = ImmutableDictionary<string, string>.Empty;
|
||||
}
|
||||
|
||||
[GeneratedImmutable(GenerateEquality = true)]
|
||||
public partial class MyEntityForStringComparison
|
||||
{
|
||||
[EqualityComparerOptions(StringMode = StringComparerMode.Default)]
|
||||
public string DefaultMode { get; }
|
||||
|
||||
[EqualityComparerOptions(StringMode = StringComparerMode.IgnoreCase)]
|
||||
public string IgnoreCase { get; }
|
||||
|
||||
[EqualityComparerOptions(StringMode = StringComparerMode.EmptyEqualsNull)]
|
||||
public string EmptyEqualsNull { get; }
|
||||
|
||||
[EqualityComparerOptions(StringMode = StringComparerMode.EmptyEqualsNull | StringComparerMode.IgnoreCase)]
|
||||
public string EmptyEqualsNullIgnoreCase { get; }
|
||||
}
|
||||
|
||||
[GeneratedEquality]
|
||||
public partial class MyEntityForAllCollectionsAndDictionaryTypes
|
||||
{
|
||||
public MyEntityForAllCollectionsAndDictionaryTypes(
|
||||
string[] arraySorted = null,
|
||||
string[] arrayUnsorted = null,
|
||||
List<string> listSorted = null,
|
||||
List<string> listUnsorted = null,
|
||||
IImmutableList<string> readonlyCollectionSorted = null,
|
||||
IImmutableList<string> readonlyCollectionUnsorted = null,
|
||||
IImmutableDictionary<string, string> readonlyDictionary = null,
|
||||
Dictionary<string, string> dictionary = null)
|
||||
{
|
||||
ArraySorted = arraySorted;
|
||||
ArrayUnsorted = arrayUnsorted;
|
||||
ListSorted = listSorted;
|
||||
ListUnsorted = listUnsorted;
|
||||
ReadonlyCollectionSorted = readonlyCollectionSorted;
|
||||
ReadonlyCollectionUnsorted = readonlyCollectionUnsorted;
|
||||
ReadonlyDictionary = readonlyDictionary;
|
||||
Dictionary = dictionary;
|
||||
}
|
||||
|
||||
[EqualityHash]
|
||||
[EqualityComparerOptions(CollectionMode = CollectionComparerMode.Sorted)]
|
||||
public string[] ArraySorted { get; }
|
||||
|
||||
[EqualityHash]
|
||||
[EqualityComparerOptions(CollectionMode = CollectionComparerMode.Unsorted)]
|
||||
public string[] ArrayUnsorted { get; }
|
||||
|
||||
[EqualityHash]
|
||||
[EqualityComparerOptions(CollectionMode = CollectionComparerMode.Sorted)]
|
||||
public List<string> ListSorted { get; }
|
||||
|
||||
[EqualityHash]
|
||||
[EqualityComparerOptions(CollectionMode = CollectionComparerMode.Unsorted)]
|
||||
public List<string> ListUnsorted { get; }
|
||||
|
||||
[EqualityHash]
|
||||
[EqualityComparerOptions(CollectionMode = CollectionComparerMode.Sorted)]
|
||||
public IImmutableList<string> ReadonlyCollectionSorted { get; }
|
||||
|
||||
[EqualityHash]
|
||||
[EqualityComparerOptions(CollectionMode = CollectionComparerMode.Unsorted)]
|
||||
public IImmutableList<string> ReadonlyCollectionUnsorted { get; }
|
||||
|
||||
[EqualityHash]
|
||||
public IImmutableDictionary<string, string> ReadonlyDictionary { get; }
|
||||
|
||||
[EqualityHash]
|
||||
public Dictionary<string, string> Dictionary { get; }
|
||||
}
|
||||
|
||||
}
|
|
@ -180,7 +180,7 @@ namespace Uno.CodeGen.Tests
|
|||
{
|
||||
var json = JsonConvert.SerializeObject(A.Default.WithEntity(x => null).ToImmutable());
|
||||
|
||||
json.Should().BeEquivalentTo("{\"T\":null,\"Entity\":null,\"IsSomething\":true}");
|
||||
json.Should().BeEquivalentTo("{\"T\":null,\"Entity\":null,\"IsSomething\":true,\"Metadata\":null}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -188,7 +188,7 @@ namespace Uno.CodeGen.Tests
|
|||
{
|
||||
var json = JsonConvert.SerializeObject(A.Default.WithEntity(x => null));
|
||||
|
||||
json.Should().BeEquivalentTo("{\"T\":null,\"Entity\":null,\"IsSomething\":true}");
|
||||
json.Should().BeEquivalentTo("{\"T\":null,\"Entity\":null,\"IsSomething\":true,\"Metadata\":null}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
@ -30,12 +31,20 @@ namespace Uno
|
|||
[GenerateAfter("Uno.ImmutableGenerator")]
|
||||
public class EqualityGenerator : SourceGenerator
|
||||
{
|
||||
private const int CollectionModeSorted = (int)CollectionComparerMode.Sorted; // this reference won't survive compilation
|
||||
private const int CollectionModeUnsorted = (int)CollectionComparerMode.Sorted; // this reference won't survive compilation
|
||||
|
||||
private const byte StringModeIgnoreCase = (byte)StringComparerMode.IgnoreCase; // this reference won't survive compilation
|
||||
private const byte StringModeEmptyEqualsNull = (byte)StringComparerMode.EmptyEqualsNull; // this reference won't survive compilation
|
||||
|
||||
private INamedTypeSymbol _objectSymbol;
|
||||
private INamedTypeSymbol _valueTypeSymbol;
|
||||
private INamedTypeSymbol _boolSymbol;
|
||||
private INamedTypeSymbol _intSymbol;
|
||||
private INamedTypeSymbol _stringSymbol;
|
||||
private INamedTypeSymbol _arraySymbol;
|
||||
private INamedTypeSymbol _collectionSymbol;
|
||||
private INamedTypeSymbol _readonlyCollectionGenericSymbol;
|
||||
private INamedTypeSymbol _collectionGenericSymbol;
|
||||
private INamedTypeSymbol _iEquatableSymbol;
|
||||
private INamedTypeSymbol _iKeyEquatableSymbol;
|
||||
|
@ -44,6 +53,7 @@ namespace Uno
|
|||
private INamedTypeSymbol _ignoreForEqualityAttributeSymbol;
|
||||
private INamedTypeSymbol _equalityHashCodeAttributeSymbol;
|
||||
private INamedTypeSymbol _equalityKeyCodeAttributeSymbol;
|
||||
private INamedTypeSymbol _equalityComparerOptionsAttributeSymbol;
|
||||
private INamedTypeSymbol _dataAnnonationsKeyAttributeSymbol;
|
||||
private SourceGeneratorContext _context;
|
||||
|
||||
|
@ -82,8 +92,10 @@ namespace Uno
|
|||
_valueTypeSymbol = context.Compilation.GetTypeByMetadataName("System.ValueType");
|
||||
_boolSymbol = context.Compilation.GetTypeByMetadataName("System.Bool");
|
||||
_intSymbol = context.Compilation.GetTypeByMetadataName("System.Int32");
|
||||
_stringSymbol = context.Compilation.GetTypeByMetadataName("System.String");
|
||||
_arraySymbol = context.Compilation.GetTypeByMetadataName("System.Array");
|
||||
_collectionSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.ICollection");
|
||||
_readonlyCollectionGenericSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.IReadOnlyCollection`1");
|
||||
_collectionGenericSymbol = context.Compilation.GetTypeByMetadataName("System.Collections.Generic.ICollection`1");
|
||||
_iEquatableSymbol = context.Compilation.GetTypeByMetadataName("System.IEquatable`1");
|
||||
_iKeyEquatableSymbol = context.Compilation.GetTypeByMetadataName("Uno.Equality.IKeyEquatable");
|
||||
|
@ -92,11 +104,11 @@ namespace Uno
|
|||
_ignoreForEqualityAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.EqualityIgnoreAttribute");
|
||||
_equalityHashCodeAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.EqualityHashAttribute");
|
||||
_equalityKeyCodeAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.EqualityKeyAttribute");
|
||||
_equalityComparerOptionsAttributeSymbol = context.Compilation.GetTypeByMetadataName("Uno.EqualityComparerOptionsAttribute");
|
||||
_dataAnnonationsKeyAttributeSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.KeyAttribute");
|
||||
|
||||
_generateKeyEqualityCode = _iKeyEquatableSymbol != null;
|
||||
|
||||
|
||||
foreach (var type in EnumerateEqualityTypesToGenerate())
|
||||
{
|
||||
GenerateEquality(type);
|
||||
|
@ -163,7 +175,7 @@ namespace Uno
|
|||
{
|
||||
if (typeSymbol.IsReferenceType)
|
||||
{
|
||||
builder.AppendLineInvariant("if (ReferenceEquals(a, b)) return true; // Same instance");
|
||||
builder.AppendLineInvariant("if (ReferenceEquals(a, b)) return true; // Same instance or both null");
|
||||
using (builder.BlockInvariant("if (ReferenceEquals(null, a))"))
|
||||
{
|
||||
builder.AppendLineInvariant("return ReferenceEquals(null, b);");
|
||||
|
@ -408,18 +420,95 @@ namespace Uno
|
|||
|
||||
if (customComparerProperty == null)
|
||||
{
|
||||
builder.AppendLineInvariant($"// **{member.Name}** You can define a custom comparer for {member.Name} and it will be used.");
|
||||
builder.AppendLineInvariant($"// CUSTOM COMPARER>> private static IEqualityComparer<{type}> {GetCustomComparerPropertyName(member)} => <custom comparer>;");
|
||||
|
||||
using (builder.BlockInvariant(
|
||||
$"if(!System.Collections.Generic.EqualityComparer<{type}>.Default.Equals({member.Name}, other.{member.Name}))"))
|
||||
if (type.IsDictionary(out var keyType, out var valueType, out var isReadonlyDictionary))
|
||||
{
|
||||
builder.AppendLineInvariant($"return false; // {member.Name} not equal");
|
||||
var comparer = isReadonlyDictionary
|
||||
? "ReadonlyDictionaryEqualityComparer"
|
||||
: "DictionaryEqualityComparer";
|
||||
|
||||
using (builder.BlockInvariant($"if(!global::Uno.Equality.{comparer}<{type}, {keyType}, {valueType}>.Default.Equals({member.Name}, other.{member.Name}))"))
|
||||
{
|
||||
builder.AppendLineInvariant($"return false; // {member.Name} not equal");
|
||||
}
|
||||
}
|
||||
else if (type.IsCollection(out var elementType, out var isReadonlyCollection, out var isOrdered))
|
||||
{
|
||||
// Extract mode from attribute, or default following the type of the collection (if ordered)
|
||||
var optionsAttribute = member.FindAttribute(_equalityComparerOptionsAttributeSymbol);
|
||||
var mode = (int)(optionsAttribute
|
||||
?.NamedArguments
|
||||
.FirstOrDefault(na=>na.Key.Equals(nameof(EqualityComparerOptionsAttribute.CollectionMode)))
|
||||
.Value.Value
|
||||
?? (isOrdered ? CollectionModeSorted : CollectionModeUnsorted));
|
||||
|
||||
var comparer = (mode & CollectionModeSorted) == CollectionModeSorted
|
||||
? (isReadonlyCollection ? "SortedReadonlyCollectionEqualityComparer" : "SortedCollectionEqualityComparer")
|
||||
: (isReadonlyCollection ? "UnsortedReadonlyCollectionEqualityComparer" : "UnsortedCollectionEqualityComparer");
|
||||
|
||||
if (optionsAttribute == null && mode == CollectionModeSorted)
|
||||
{
|
||||
// We just show that if the collection is "sorted", because "unsorted" will always work and we don't want
|
||||
// to do a "SequenceEquals" on an non-ordered structure.
|
||||
builder.AppendLineInvariant($"// **{member.Name}** To use an _unsorted_ comparer, add the following attribute to your member:");
|
||||
builder.AppendLineInvariant("// [EqualityCollection(CollectionComparerMode.Unsorted)]");
|
||||
}
|
||||
|
||||
using (builder.BlockInvariant($"if(!global::Uno.Equality.{comparer}<{type}, {elementType}>.Default.Equals({member.Name}, other.{member.Name}))"))
|
||||
{
|
||||
builder.AppendLineInvariant($"return false; // {member.Name} not equal");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLineInvariant($"// **{member.Name}** You can define a custom comparer for {member.Name} and it will be used.");
|
||||
builder.AppendLineInvariant($"// CUSTOM COMPARER>> private static IEqualityComparer<{type}> {GetCustomComparerPropertyName(member)} => <custom comparer>;");
|
||||
|
||||
if (type.Equals(_stringSymbol))
|
||||
{
|
||||
// Extract mode from attribute, or default following the type of the collection (if ordered)
|
||||
var optionsAttribute = member.FindAttribute(_equalityComparerOptionsAttributeSymbol);
|
||||
var mode = (byte) (optionsAttribute
|
||||
?.NamedArguments
|
||||
.FirstOrDefault(na => na.Key.Equals(nameof(EqualityComparerOptionsAttribute.StringMode)))
|
||||
.Value.Value
|
||||
?? default(byte));
|
||||
|
||||
var comparer = (mode & StringModeIgnoreCase) == StringModeIgnoreCase
|
||||
? "System.StringComparer.OrdinalIgnoreCase"
|
||||
: "System.StringComparer.Ordinal";
|
||||
|
||||
var emptyEqualsNull = (mode & StringModeEmptyEqualsNull) == StringModeEmptyEqualsNull;
|
||||
var emptyCheck = emptyEqualsNull
|
||||
? $"(string.IsNullOrWhiteSpace({member.Name}) != string.IsNullOrWhiteSpace(other.{member.Name})) || !string.IsNullOrWhiteSpace({member.Name}) && "
|
||||
: "";
|
||||
var nullCoalescing = emptyEqualsNull ? " ?? \"\" " : "";
|
||||
|
||||
using (builder.BlockInvariant($"if({emptyCheck}!{comparer}.Equals({member.Name}{nullCoalescing}, other.{member.Name}{nullCoalescing}))"))
|
||||
{
|
||||
builder.AppendLineInvariant($"return false; // {member.Name} not equal");
|
||||
}
|
||||
}
|
||||
else if (type.IsPrimitive())
|
||||
{
|
||||
using (builder.BlockInvariant($"if({member.Name} != other.{member.Name})"))
|
||||
{
|
||||
builder.AppendLineInvariant($"return false; // {member.Name} not equal");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (builder.BlockInvariant($"if(!System.Collections.Generic.EqualityComparer<{type}>.Default.Equals({member.Name}, other.{member.Name}))"))
|
||||
{
|
||||
builder.AppendLineInvariant($"return false; // {member.Name} not equal");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (builder.BlockInvariant($"if(!{customComparerProperty.Name}.Equals({member.Name}, other.{member.Name})) // Using custom comparer provided by `{customComparerProperty.Name}()`."))
|
||||
builder.AppendLineInvariant($"// **{member.Name}** using custom comparer provided by `{customComparerProperty.Name}()` {customComparerProperty.Locations.FirstOrDefault()}");
|
||||
using (builder.BlockInvariant($"if(!{customComparerProperty.Name}.Equals({member.Name}, other.{member.Name}))"))
|
||||
{
|
||||
builder.AppendLineInvariant($"return false; // {member.Name} not equal");
|
||||
}
|
||||
|
@ -486,12 +575,7 @@ namespace Uno
|
|||
builder.AppendLineInvariant($"// CUSTOM COMPARER>> private static IEqualityComparer<{type}> {GetCustomComparerPropertyName(member)} => <custom comparer>;");
|
||||
}
|
||||
|
||||
var definition = type;
|
||||
|
||||
while (definition is INamedTypeSymbol nts && !nts.ConstructedFrom.Equals(definition))
|
||||
{
|
||||
definition = nts.ConstructedFrom;
|
||||
}
|
||||
var definition = type.GetDefinitionType();
|
||||
|
||||
string getHashCode;
|
||||
if (customHashMethod != null)
|
||||
|
@ -514,34 +598,45 @@ namespace Uno
|
|||
{
|
||||
getHashCode = $"{member.Name}.Length";
|
||||
}
|
||||
else if (type.IsDictionary(out var dictionaryKeyType, out var dictionaryValueType, out var isReadonlyDictionary))
|
||||
{
|
||||
getHashCode = isReadonlyDictionary
|
||||
? $"((global::System.Collections.Generic.IReadOnlyDictionary<{dictionaryKeyType}, {dictionaryValueType}>){member.Name}).Count"
|
||||
: $"((global::System.Collections.Generic.IDictionary<{dictionaryKeyType}, {dictionaryValueType}>){member.Name}).Count";
|
||||
}
|
||||
else if (type.IsCollection(out var collectionElementType, out var isReadonlyCollection, out var _))
|
||||
{
|
||||
getHashCode = isReadonlyCollection
|
||||
? $"((global::System.Collections.Generic.IReadOnlyCollection<{collectionElementType}>){member.Name}).Count"
|
||||
: $"((global::System.Collections.Generic.ICollection<{collectionElementType}>){member.Name}).Count";
|
||||
}
|
||||
else if (definition.Equals(_collectionSymbol)
|
||||
|| definition.DerivesFromType(_collectionSymbol))
|
||||
{
|
||||
getHashCode = $"((global::System.Collections.ICollection){member.Name}).Count";
|
||||
}
|
||||
else if (definition.Equals(_collectionGenericSymbol)
|
||||
|| definition.DerivesFromType(_collectionGenericSymbol))
|
||||
{
|
||||
getHashCode = $"((global::System.Collections.Generic.ICollection<{type.GetTypeArgumentNames().FirstOrDefault()}>){member.Name}).Count";
|
||||
}
|
||||
else
|
||||
{
|
||||
var getHashCodeMember = definition
|
||||
.GetMembers("GetHashCode")
|
||||
.OfType<IMethodSymbol>()
|
||||
.Where(m => !m.IsStatic)
|
||||
.Where(m => m.IsOverride)
|
||||
.Where(m => !m.IsAbstract)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (getHashCodeMember == null)
|
||||
var isGeneratedEquality = type.FindAttribute(_generatedEqualityAttributeSymbol) != null;
|
||||
if (!isGeneratedEquality)
|
||||
{
|
||||
builder.AppendLineInvariant(
|
||||
$"#warning Type `{type.GetDisplayFriendlyName()}` of member `{member.Name}` " +
|
||||
"doesn't implements .GetHashCode(): it won't be used for hash computation. " +
|
||||
$"If you can change the type {type}, you should use a custom hash method or " +
|
||||
"a custom comparer.");
|
||||
continue;
|
||||
var getHashCodeMember = definition
|
||||
.GetMembers("GetHashCode")
|
||||
.OfType<IMethodSymbol>()
|
||||
.Where(m => !m.IsStatic)
|
||||
.Where(m => m.IsOverride)
|
||||
.Where(m => !m.IsAbstract)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (getHashCodeMember == null)
|
||||
{
|
||||
builder.AppendLineInvariant(
|
||||
$"#warning Type `{type.GetDisplayFriendlyName()}` of member `{member.Name}` " +
|
||||
"doesn't implements .GetHashCode(): it won't be used for hash computation. " +
|
||||
$"If you can't change the type {type}, you should use a custom hash method or " +
|
||||
"a custom comparer. Alternatively, use something else for hash computation.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
getHashCode = $"{member.Name}.GetHashCode()";
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
@ -27,18 +29,8 @@ namespace Uno.Helpers
|
|||
return type.GetType().Name.Equals("SourceTypeParameterSymbol");
|
||||
}
|
||||
|
||||
public static bool IsImmutable(this ITypeSymbol type, bool treatArrayAsImmutable)
|
||||
public static bool IsPrimitive(this ITypeSymbol type)
|
||||
{
|
||||
foreach (var attribute in type.GetAttributes())
|
||||
{
|
||||
switch (attribute.AttributeClass.ToString())
|
||||
{
|
||||
case "Uno.ImmutableAttribute":
|
||||
case "Uno.GeneratedImmutableAttribute":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type.SpecialType)
|
||||
{
|
||||
case SpecialType.System_Boolean:
|
||||
|
@ -60,88 +52,257 @@ namespace Uno.Helpers
|
|||
case SpecialType.System_UInt64:
|
||||
case SpecialType.System_UIntPtr:
|
||||
return true;
|
||||
case SpecialType.None:
|
||||
case SpecialType.System_Collections_Generic_IReadOnlyCollection_T:
|
||||
case SpecialType.System_Collections_Generic_IReadOnlyList_T:
|
||||
break; // need further validation
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type is IArrayTypeSymbol arrayType)
|
||||
{
|
||||
return arrayType.ElementType?.IsImmutable(treatArrayAsImmutable) ?? false;
|
||||
}
|
||||
|
||||
var definitionType = type;
|
||||
|
||||
while ((definitionType as INamedTypeSymbol)?.ConstructedFrom.Equals(definitionType) == false)
|
||||
{
|
||||
definitionType = ((INamedTypeSymbol)definitionType).ConstructedFrom;
|
||||
}
|
||||
|
||||
switch (definitionType.ToString())
|
||||
{
|
||||
case "System.Attribute": // strange, but valid
|
||||
case "System.DateTime":
|
||||
case "System.DateTimeOffset":
|
||||
case "System.TimeSpan":
|
||||
case "System.Type":
|
||||
case "System.Uri":
|
||||
case "System.Version":
|
||||
return true;
|
||||
|
||||
// .NET framework
|
||||
case "System.Collections.Generic.IReadOnlyList<T>":
|
||||
case "System.Collections.Generic.IReadOnlyCollection<T>":
|
||||
case "System.Nullable<T>":
|
||||
case "System.Tuple<T>":
|
||||
// System.Collections.Immutable (nuget package)
|
||||
case "System.Collections.Immutable.IImmutableList<T>":
|
||||
case "System.Collections.Immutable.IImmutableQueue<T>":
|
||||
case "System.Collections.Immutable.IImmutableSet<T>":
|
||||
case "System.Collections.Immutable.IImmutableStack<T>":
|
||||
case "System.Collections.Immutable.ImmutableArray<T>":
|
||||
case "System.Collections.Immutable.ImmutableHashSet<T>":
|
||||
case "System.Collections.Immutable.ImmutableList<T>":
|
||||
case "System.Collections.Immutable.ImmutableQueue<T>":
|
||||
case "System.Collections.Immutable.ImmutableSortedSet<T>":
|
||||
case "System.Collections.Immutable.ImmutableStack<T>":
|
||||
{
|
||||
var argumentParameter = (type as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
||||
return argumentParameter == null || argumentParameter.IsImmutable(treatArrayAsImmutable);
|
||||
}
|
||||
case "System.Collections.Immutable.IImmutableDictionary<TKey, TValue>":
|
||||
case "System.Collections.Immutable.ImmutableDictionary<TKey, TValue>":
|
||||
case "System.Collections.Immutable.ImmutableSortedDictionary<TKey, TValue>":
|
||||
{
|
||||
var keyTypeParameter = (type as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
||||
var valueTypeParameter = (type as INamedTypeSymbol)?.TypeArguments.Skip(1).FirstOrDefault();
|
||||
return (keyTypeParameter == null || keyTypeParameter.IsImmutable(treatArrayAsImmutable))
|
||||
&& (valueTypeParameter == null || valueTypeParameter.IsImmutable(treatArrayAsImmutable));
|
||||
}
|
||||
}
|
||||
|
||||
switch (definitionType.GetType().Name)
|
||||
{
|
||||
case "TupleTypeSymbol":
|
||||
return true; // tuples are immutables
|
||||
}
|
||||
|
||||
switch (definitionType.BaseType?.ToString())
|
||||
{
|
||||
case "System.Enum":
|
||||
return true;
|
||||
case "System.Array":
|
||||
return treatArrayAsImmutable;
|
||||
}
|
||||
|
||||
if (definitionType.IsReferenceType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static ImmutableDictionary<(ITypeSymbol, bool), bool> _isImmutable =
|
||||
ImmutableDictionary<(ITypeSymbol, bool), bool>.Empty;
|
||||
|
||||
public static bool IsImmutable(this ITypeSymbol type, bool treatArrayAsImmutable)
|
||||
{
|
||||
bool GetIsImmutable((ITypeSymbol type, bool treatArrayAsImmutable) x)
|
||||
{
|
||||
var (t, asImmutable) = x;
|
||||
foreach (var attribute in t.GetAttributes())
|
||||
{
|
||||
switch (attribute.AttributeClass.ToString())
|
||||
{
|
||||
case "Uno.ImmutableAttribute":
|
||||
case "Uno.GeneratedImmutableAttribute":
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (t.IsPrimitive())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (t.SpecialType)
|
||||
{
|
||||
case SpecialType.None:
|
||||
case SpecialType.System_Collections_Generic_IReadOnlyCollection_T:
|
||||
case SpecialType.System_Collections_Generic_IReadOnlyList_T:
|
||||
break; // need further validation
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t is IArrayTypeSymbol arrayType)
|
||||
{
|
||||
return arrayType.ElementType?.IsImmutable(asImmutable) ?? false;
|
||||
}
|
||||
|
||||
var definitionType = t.GetDefinitionType();
|
||||
|
||||
switch (definitionType.ToString())
|
||||
{
|
||||
case "System.Attribute": // strange, but valid
|
||||
case "System.DateTime":
|
||||
case "System.DateTimeOffset":
|
||||
case "System.TimeSpan":
|
||||
case "System.Type":
|
||||
case "System.Uri":
|
||||
case "System.Version":
|
||||
return true;
|
||||
|
||||
// .NET framework
|
||||
case "System.Collections.Generic.IReadOnlyList<T>":
|
||||
case "System.Collections.Generic.IReadOnlyCollection<T>":
|
||||
case "System.Nullable<T>":
|
||||
case "System.Tuple<T>":
|
||||
// System.Collections.Immutable (nuget package)
|
||||
case "System.Collections.Immutable.IImmutableList<T>":
|
||||
case "System.Collections.Immutable.IImmutableQueue<T>":
|
||||
case "System.Collections.Immutable.IImmutableSet<T>":
|
||||
case "System.Collections.Immutable.IImmutableStack<T>":
|
||||
case "System.Collections.Immutable.ImmutableArray<T>":
|
||||
case "System.Collections.Immutable.ImmutableHashSet<T>":
|
||||
case "System.Collections.Immutable.ImmutableList<T>":
|
||||
case "System.Collections.Immutable.ImmutableQueue<T>":
|
||||
case "System.Collections.Immutable.ImmutableSortedSet<T>":
|
||||
case "System.Collections.Immutable.ImmutableStack<T>":
|
||||
{
|
||||
var argumentParameter = (t as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
||||
return argumentParameter == null || argumentParameter.IsImmutable(asImmutable);
|
||||
}
|
||||
case "System.Collections.Immutable.IImmutableDictionary<TKey, TValue>":
|
||||
case "System.Collections.Immutable.ImmutableDictionary<TKey, TValue>":
|
||||
case "System.Collections.Immutable.ImmutableSortedDictionary<TKey, TValue>":
|
||||
{
|
||||
var keyTypeParameter = (t as INamedTypeSymbol)?.TypeArguments.FirstOrDefault();
|
||||
var valueTypeParameter = (t as INamedTypeSymbol)?.TypeArguments.Skip(1).FirstOrDefault();
|
||||
return (keyTypeParameter == null || keyTypeParameter.IsImmutable(asImmutable))
|
||||
&& (valueTypeParameter == null || valueTypeParameter.IsImmutable(asImmutable));
|
||||
}
|
||||
}
|
||||
|
||||
switch (definitionType.GetType().Name)
|
||||
{
|
||||
case "TupleTypeSymbol":
|
||||
return true; // tuples are immutables
|
||||
}
|
||||
|
||||
switch (definitionType.BaseType?.ToString())
|
||||
{
|
||||
case "System.Enum":
|
||||
return true;
|
||||
case "System.Array":
|
||||
return asImmutable;
|
||||
}
|
||||
|
||||
if (definitionType.IsReferenceType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return ImmutableInterlocked.GetOrAdd(ref _isImmutable, (type, treatArrayAsImmutable), GetIsImmutable);
|
||||
}
|
||||
|
||||
public static ITypeSymbol GetDefinitionType(this ITypeSymbol type)
|
||||
{
|
||||
var definitionType = type;
|
||||
|
||||
while ((definitionType as INamedTypeSymbol)?.ConstructedFrom.Equals(definitionType) == false)
|
||||
{
|
||||
definitionType = ((INamedTypeSymbol) definitionType).ConstructedFrom;
|
||||
}
|
||||
|
||||
return definitionType;
|
||||
}
|
||||
|
||||
private static ImmutableDictionary<ITypeSymbol, (ITypeSymbol, bool, bool)> _isCollection =
|
||||
ImmutableDictionary<ITypeSymbol, (ITypeSymbol, bool, bool)>.Empty;
|
||||
|
||||
public static bool IsCollection(this ITypeSymbol type, out ITypeSymbol elementType, out bool isReadonlyCollection, out bool isOrdered)
|
||||
{
|
||||
(ITypeSymbol elementType, bool isReadonlyCollection, bool isOrdered) GetTypeArgumentIfItIsACollection(INamedTypeSymbol t)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
var interfaceDefinition = t.GetDefinitionType();
|
||||
|
||||
switch (interfaceDefinition.ToString())
|
||||
{
|
||||
case "System.Collections.Immutable.ImmutableHashSet<T>":
|
||||
case "System.Collections.Immutable.IImmutableSet<T>":
|
||||
{
|
||||
return (t.TypeArguments[0], true, false);
|
||||
}
|
||||
case "System.Collections.Immutable.IImmutableList<T>":
|
||||
case "System.Collections.Immutable.IImmutableQueue<T>":
|
||||
case "System.Collections.Immutable.IImmutableStack<T>":
|
||||
case "System.Collections.Immutable.ImmutableArray<T>":
|
||||
case "System.Collections.Immutable.ImmutableList<T>":
|
||||
case "System.Collections.Immutable.ImmutableQueue<T>":
|
||||
case "System.Collections.Immutable.ImmutableSortedSet<T>":
|
||||
case "System.Collections.Immutable.ImmutableStack<T>":
|
||||
{
|
||||
return (t.TypeArguments[0], true, true);
|
||||
}
|
||||
case "System.Collections.Generic.IReadOnlyCollection<T>":
|
||||
{
|
||||
return (t.TypeArguments[0], true, true);
|
||||
}
|
||||
case "System.Collections.Generic.HashSet<T>":
|
||||
{
|
||||
return (t.TypeArguments[0], false, false);
|
||||
}
|
||||
case "System.Collections.Generic.ICollection<T>":
|
||||
case "System.Collections.Generic.List<T>":
|
||||
case "System.Collections.Generic.LinkedList<T>":
|
||||
case "System.Collections.Generic.Queue<T>":
|
||||
case "System.Collections.Generic.SortedSet<T>":
|
||||
case "System.Collections.Generic.Stack<T>":
|
||||
{
|
||||
return (t.TypeArguments[0], false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (default(ITypeSymbol), default(bool), default(bool));
|
||||
}
|
||||
|
||||
(ITypeSymbol elementType, bool isReadonlyCollection, bool isOrdered) GetIsCollection(ITypeSymbol t)
|
||||
{
|
||||
var r = GetTypeArgumentIfItIsACollection(t as INamedTypeSymbol);
|
||||
|
||||
if (r.elementType != null)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
foreach (var @interface in t.AllInterfaces)
|
||||
{
|
||||
r = GetTypeArgumentIfItIsACollection(@interface);
|
||||
if (r.elementType != null)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return (default(ITypeSymbol), default(bool), default(bool));
|
||||
}
|
||||
|
||||
(elementType, isReadonlyCollection, isOrdered) = ImmutableInterlocked.GetOrAdd(ref _isCollection, type, GetIsCollection);
|
||||
return elementType != null;
|
||||
}
|
||||
|
||||
private static ImmutableDictionary<ITypeSymbol, (ITypeSymbol, ITypeSymbol, bool)> _isDictionary =
|
||||
ImmutableDictionary<ITypeSymbol, (ITypeSymbol, ITypeSymbol, bool)>.Empty;
|
||||
|
||||
public static bool IsDictionary(this ITypeSymbol type, out ITypeSymbol keyType, out ITypeSymbol valueType, out bool isReadonlyDictionary)
|
||||
{
|
||||
(ITypeSymbol keyType, ITypeSymbol valueType, bool isReadonlyDictionary) GetTypeArgumentIfItIsACollection(INamedTypeSymbol t)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
var interfaceDefinition = t.GetDefinitionType();
|
||||
|
||||
switch (interfaceDefinition.ToString())
|
||||
{
|
||||
case "System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>":
|
||||
{
|
||||
return (t.TypeArguments[0], t.TypeArguments[1], true);
|
||||
}
|
||||
case "System.Collections.Generic.IDictionary<TKey, TValue>":
|
||||
{
|
||||
return (t.TypeArguments[0], t.TypeArguments[1], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (null, null, false);
|
||||
}
|
||||
|
||||
(ITypeSymbol keyType, ITypeSymbol valueType, bool isReadonlyDictionary) GetIsACollection(ITypeSymbol t)
|
||||
{
|
||||
var r = GetTypeArgumentIfItIsACollection(t as INamedTypeSymbol);
|
||||
|
||||
if (r.keyType != null)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
foreach (var @interface in t.AllInterfaces)
|
||||
{
|
||||
r = GetTypeArgumentIfItIsACollection(@interface);
|
||||
if (r.keyType != null)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return (null, null, false);
|
||||
}
|
||||
|
||||
(keyType, valueType, isReadonlyDictionary) = ImmutableInterlocked.GetOrAdd(ref _isDictionary, type, GetIsACollection);
|
||||
return keyType != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
namespace Uno
|
||||
{
|
||||
/// <summary>
|
||||
/// Use to qualify the collection mode in <see cref="EqualityComparerOptionsAttribute"/> attribute.
|
||||
/// </summary>
|
||||
public enum CollectionComparerMode : int
|
||||
{
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Use a comparer which allows for a different ordering between collections.
|
||||
/// </summary>
|
||||
Unsorted = 0b0000,
|
||||
|
||||
/// <summary>
|
||||
/// Use a comparer which checks the ordering in the collections.
|
||||
/// </summary>
|
||||
Sorted = 0b0001,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Uno.Equality
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEqualityComparer{T}"/> implementation for evaluating dictionaries
|
||||
/// </summary>
|
||||
public class DictionaryEqualityComparer<TDict, TKey, TValue> : IEqualityComparer<TDict>
|
||||
where TDict : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly bool _nullIsEmpty;
|
||||
private readonly IEqualityComparer<TValue> _valueComparer;
|
||||
|
||||
public static IEqualityComparer<TDict> Default { get; } = new DictionaryEqualityComparer<TDict, TKey, TValue>();
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="valueComparer">Comparer to use to comparer an enumerated item</param>
|
||||
/// <param name="nullIsEmpty">If null value should be compated as empty collection</param>
|
||||
public DictionaryEqualityComparer(IEqualityComparer<TValue> valueComparer = null, bool nullIsEmpty = true)
|
||||
{
|
||||
_nullIsEmpty = nullIsEmpty;
|
||||
_valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(TDict x, TDict y)
|
||||
{
|
||||
return (Equals(x, y, _valueComparer, _nullIsEmpty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static (instance-less) equality check
|
||||
/// </summary>
|
||||
public static bool Equals(TDict x, TDict y, IEqualityComparer<TValue> valueComparer, bool nullIsEmpty = true)
|
||||
{
|
||||
if (valueComparer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(valueComparer));
|
||||
}
|
||||
|
||||
if (nullIsEmpty)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null || y.Count == 0;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return x.Count == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x.Count != y.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var keyValue in x)
|
||||
{
|
||||
if (!y.TryGetValue(keyValue.Key, out var yValue))
|
||||
{
|
||||
return false; // key not found: dictionaries are not equal.
|
||||
}
|
||||
|
||||
if (!valueComparer.Equals(keyValue.Value, yValue))
|
||||
{
|
||||
return false; // value for this key is different.
|
||||
}
|
||||
}
|
||||
|
||||
return true; // no difference found
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetHashCode(TDict obj)
|
||||
{
|
||||
return obj?.Count ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
|
||||
namespace Uno
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute to put on a collection field/property to specify the kind
|
||||
/// of collection comparer to use.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
|
||||
public class EqualityComparerOptionsAttribute : Attribute
|
||||
{
|
||||
public CollectionComparerMode CollectionMode { get; set; } = CollectionComparerMode.Default;
|
||||
|
||||
public StringComparerMode StringMode { get; set; } = StringComparerMode.Default;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Uno.Equality
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEqualityComparer{T}"/> implementation for evaluating dictionaries
|
||||
/// </summary>
|
||||
public class ReadonlyDictionaryEqualityComparer<TDict, TKey, TValue> : IEqualityComparer<TDict>
|
||||
where TDict : IReadOnlyDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly bool _nullIsEmpty;
|
||||
private readonly IEqualityComparer<TValue> _valueComparer;
|
||||
|
||||
public static IEqualityComparer<TDict> Default { get; } = new ReadonlyDictionaryEqualityComparer<TDict, TKey, TValue>();
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="valueComparer">Comparer to use to comparer an enumerated item</param>
|
||||
/// <param name="nullIsEmpty">If null value should be compated as empty collection</param>
|
||||
public ReadonlyDictionaryEqualityComparer(IEqualityComparer<TValue> valueComparer = null, bool nullIsEmpty = true)
|
||||
{
|
||||
_nullIsEmpty = nullIsEmpty;
|
||||
_valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(TDict x, TDict y)
|
||||
{
|
||||
return (Equals(x, y, _valueComparer, _nullIsEmpty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static (instance-less) equality check
|
||||
/// </summary>
|
||||
public static bool Equals(TDict x, TDict y, IEqualityComparer<TValue> valueComparer, bool nullIsEmpty = true)
|
||||
{
|
||||
if (valueComparer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(valueComparer));
|
||||
}
|
||||
|
||||
if (nullIsEmpty)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null || y.Count == 0;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return x.Count == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x.Count != y.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var keyValue in x)
|
||||
{
|
||||
if (!y.TryGetValue(keyValue.Key, out var yValue))
|
||||
{
|
||||
return false; // key not found: dictionaries are not equal.
|
||||
}
|
||||
|
||||
if (!valueComparer.Equals(keyValue.Value, yValue))
|
||||
{
|
||||
return false; // value for this key is different.
|
||||
}
|
||||
}
|
||||
|
||||
return true; // no difference found
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetHashCode(TDict obj)
|
||||
{
|
||||
return obj?.Count ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Uno.Equality
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEqualityComparer{T}"/> implementation for evaluating sorted collections
|
||||
/// </summary>
|
||||
public class SortedCollectionEqualityComparer<TCollection, T> : IEqualityComparer<TCollection>
|
||||
where TCollection : ICollection<T>
|
||||
{
|
||||
private readonly IEqualityComparer<T> _itemComparer;
|
||||
private readonly bool _nullIsEmpty;
|
||||
|
||||
public static IEqualityComparer<TCollection> Default { get; } = new SortedCollectionEqualityComparer<TCollection, T>();
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="itemComparer">Comparer to use to comparer an enumerated item</param>
|
||||
/// <param name="nullIsEmpty">If null value should be compated as empty collection</param>
|
||||
public SortedCollectionEqualityComparer(IEqualityComparer<T> itemComparer = null, bool nullIsEmpty = true)
|
||||
{
|
||||
_itemComparer = itemComparer ?? EqualityComparer<T>.Default;
|
||||
_nullIsEmpty = nullIsEmpty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(TCollection x, TCollection y)
|
||||
{
|
||||
return(Equals(x, y, _itemComparer, _nullIsEmpty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static (instance-less) equality check
|
||||
/// </summary>
|
||||
public static bool Equals(TCollection x, TCollection y, IEqualityComparer<T> itemComparer, bool nullIsEmpty = true)
|
||||
{
|
||||
if (itemComparer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(itemComparer));
|
||||
}
|
||||
|
||||
if (nullIsEmpty)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null || y.Count == 0;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return x.Count == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x.Count != y.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var sequenceEqual = x.SequenceEqual(y, itemComparer);
|
||||
return sequenceEqual;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetHashCode(TCollection obj)
|
||||
{
|
||||
return obj?.Count ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Uno.Equality
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEqualityComparer{T}"/> implementation for evaluating sorted collections
|
||||
/// </summary>
|
||||
public class SortedReadonlyCollectionEqualityComparer<TCollection, T> : IEqualityComparer<TCollection>
|
||||
where TCollection : IReadOnlyCollection<T>
|
||||
{
|
||||
private readonly IEqualityComparer<T> _itemComparer;
|
||||
private readonly bool _nullIsEmpty;
|
||||
|
||||
public static IEqualityComparer<TCollection> Default { get; } = new SortedReadonlyCollectionEqualityComparer<TCollection, T>();
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="itemComparer">Comparer to use to comparer an enumerated item</param>
|
||||
/// <param name="nullIsEmpty">If null value should be compated as empty collection</param>
|
||||
public SortedReadonlyCollectionEqualityComparer(IEqualityComparer<T> itemComparer = null, bool nullIsEmpty = true)
|
||||
{
|
||||
_itemComparer = itemComparer ?? EqualityComparer<T>.Default;
|
||||
_nullIsEmpty = nullIsEmpty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(TCollection x, TCollection y)
|
||||
{
|
||||
return (Equals(x, y, _itemComparer, _nullIsEmpty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static (instance-less) equality check
|
||||
/// </summary>
|
||||
public static bool Equals(TCollection x, TCollection y, IEqualityComparer<T> itemComparer, bool nullIsEmpty = true)
|
||||
{
|
||||
if (itemComparer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(itemComparer));
|
||||
}
|
||||
|
||||
if (nullIsEmpty)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null || y.Count == 0;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return x.Count == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x.Count != y.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var sequenceEqual = x.SequenceEqual(y, itemComparer);
|
||||
return sequenceEqual;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetHashCode(TCollection obj)
|
||||
{
|
||||
return obj?.Count ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
|
||||
namespace Uno
|
||||
{
|
||||
/// <summary>
|
||||
/// Use to qualify the string mode in <see cref="EqualityComparerOptionsAttribute"/> attribute.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum StringComparerMode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// No modes activated
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Ignore casing when comparing
|
||||
/// </summary>
|
||||
IgnoreCase = 0b0001,
|
||||
|
||||
/// <summary>
|
||||
/// Treat empty/white strings and null as equal
|
||||
/// </summary>
|
||||
EmptyEqualsNull = 0b0010,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Uno.Equality
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEqualityComparer{T}"/> implementation for evaluating unsorted collections
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Will produce weird result on collection with repeated items (should be a "Set")
|
||||
/// </remarks>
|
||||
public class UnsortedCollectionEqualityComparer<TCollection, T> : IEqualityComparer<TCollection>
|
||||
where TCollection : ICollection<T>
|
||||
{
|
||||
private readonly IEqualityComparer<T> _itemComparer;
|
||||
private readonly bool _nullIsEmpty;
|
||||
|
||||
public static IEqualityComparer<TCollection> Default { get; } = new UnsortedCollectionEqualityComparer<TCollection, T>();
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="itemComparer">Comparer to use to comparer an enumerated item</param>
|
||||
/// <param name="nullIsEmpty">If null value should be compated as empty collection</param>
|
||||
public UnsortedCollectionEqualityComparer(IEqualityComparer<T> itemComparer = null, bool nullIsEmpty = true)
|
||||
{
|
||||
_itemComparer = itemComparer ?? EqualityComparer<T>.Default;
|
||||
_nullIsEmpty = nullIsEmpty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(TCollection x, TCollection y)
|
||||
{
|
||||
return (Equals(x, y, _itemComparer, _nullIsEmpty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static (instance-less) equality check
|
||||
/// </summary>
|
||||
public static bool Equals(TCollection x, TCollection y, IEqualityComparer<T> itemComparer, bool nullIsEmpty = true)
|
||||
{
|
||||
if (itemComparer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(itemComparer));
|
||||
}
|
||||
|
||||
if (nullIsEmpty)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null || y.Count == 0;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return x.Count == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x.Count != y.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return x.All(item => y.Contains(item, itemComparer));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetHashCode(TCollection obj)
|
||||
{
|
||||
return obj?.Count ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Uno.Equality
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEqualityComparer{T}"/> implementation for evaluating unsorted collections
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Will produce weird result on collection with repeated items (should be a "Set")
|
||||
/// </remarks>
|
||||
public class UnsortedReadonlyCollectionEqualityComparer<TCollection, T> : IEqualityComparer<TCollection>
|
||||
where TCollection : IReadOnlyCollection<T>
|
||||
{
|
||||
private readonly IEqualityComparer<T> _itemComparer;
|
||||
private readonly bool _nullIsEmpty;
|
||||
|
||||
public static IEqualityComparer<TCollection> Default { get; } = new UnsortedReadonlyCollectionEqualityComparer<TCollection, T>();
|
||||
|
||||
/// <summary>
|
||||
/// ctor
|
||||
/// </summary>
|
||||
/// <param name="itemComparer">Comparer to use to comparer an enumerated item</param>
|
||||
/// <param name="nullIsEmpty">If null value should be compated as empty collection</param>
|
||||
public UnsortedReadonlyCollectionEqualityComparer(IEqualityComparer<T> itemComparer = null, bool nullIsEmpty = true)
|
||||
{
|
||||
_itemComparer = itemComparer ?? EqualityComparer<T>.Default;
|
||||
_nullIsEmpty = nullIsEmpty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(TCollection x, TCollection y)
|
||||
{
|
||||
return (Equals(x, y, _itemComparer, _nullIsEmpty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static (instance-less) equality check
|
||||
/// </summary>
|
||||
public static bool Equals(TCollection x, TCollection y, IEqualityComparer<T> itemComparer, bool nullIsEmpty = true)
|
||||
{
|
||||
if (itemComparer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(itemComparer));
|
||||
}
|
||||
|
||||
if (nullIsEmpty)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null || y.Count == 0;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return x.Count == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return y == null;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x.Count != y.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return x.All(item => y.Contains(item, itemComparer));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int GetHashCode(TCollection obj)
|
||||
{
|
||||
return obj?.Count ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче