Repository: ValeraT1982/ObjectsComparer
Branch: master
Commit: b106e48c4679
Files: 126
Total size: 369.9 KB
Directory structure:
gitextract_agfyhd9m/
├── .gitignore
├── LICENSE
├── ObjectsComparer/
│ ├── ObjectsComparer/
│ │ ├── AbstractComparer.cs
│ │ ├── AbstractComparer~1.cs
│ │ ├── Attributes/
│ │ │ └── IgnoreInComparisonAttribute.cs
│ │ ├── BaseComparer.cs
│ │ ├── Comparer.cs
│ │ ├── ComparerOverridesCollection.cs
│ │ ├── ComparersFactory.cs
│ │ ├── Comparer~1.cs
│ │ ├── ComparisonSettings.cs
│ │ ├── CustomComparers/
│ │ │ ├── AbstractDynamicObjectsComprer.cs
│ │ │ ├── AbstractEnumerablesComparer.cs
│ │ │ ├── CompilerGeneratedObjectComparer.cs
│ │ │ ├── DynamicObjectComparer.cs
│ │ │ ├── EnumerablesComparer.cs
│ │ │ ├── EnumerablesComparer~1.cs
│ │ │ ├── ExpandoObjectComparer.cs
│ │ │ ├── GenericEnumerablesComparer.cs
│ │ │ ├── HashSetsComparer.cs
│ │ │ ├── HashSetsComparer~1.cs
│ │ │ ├── IComparerWithCondition.cs
│ │ │ ├── MultidimensionalArrayComparer~1.cs
│ │ │ ├── MultidimensionalArraysComparer.cs
│ │ │ └── TypesComparer.cs
│ │ ├── Difference.cs
│ │ ├── DifferenceTypes.cs
│ │ ├── Exceptions/
│ │ │ ├── AmbiguousComparerOverrideResolutionException.cs
│ │ │ └── ValueComparerExistsException.cs
│ │ ├── IBaseComparer.cs
│ │ ├── IComparer.cs
│ │ ├── IComparersFactory.cs
│ │ ├── IComparer~1.cs
│ │ ├── LICENSE
│ │ ├── ObjectsComparer.csproj
│ │ ├── ObjectsComparer.csproj.DotSettings
│ │ ├── Properties/
│ │ │ └── AssemblyInfo.cs
│ │ ├── Utils/
│ │ │ ├── MemberInfoExtensions.cs
│ │ │ ├── PropertyHelper.cs
│ │ │ └── TypeExtensions.cs
│ │ └── ValueComparers/
│ │ ├── AbstractValueComparer.cs
│ │ ├── AbstractValueComparer~1.cs
│ │ ├── DefaultValueComparer.cs
│ │ ├── DefaultValueValueComparer.cs
│ │ ├── DoNotCompareValueComparer.cs
│ │ ├── DynamicValueComparer.cs
│ │ ├── IValueComparer.cs
│ │ ├── IValueComparer~1.cs
│ │ ├── IgnoreCaseStringsValueComparer.cs
│ │ ├── NulableStringsValueComparer.cs
│ │ ├── ToStringComparer.cs
│ │ └── UriComparer.cs
│ ├── ObjectsComparer.Examples/
│ │ ├── BasicExamples/
│ │ │ ├── BasicExampleTests.cs
│ │ │ └── ClassA.cs
│ │ ├── Example1/
│ │ │ ├── Error.cs
│ │ │ ├── Example1Tests.cs
│ │ │ └── Message.cs
│ │ ├── Example2/
│ │ │ ├── Example2Tests.cs
│ │ │ ├── MyComparersFactory.cs
│ │ │ ├── Person.cs
│ │ │ └── PhoneNumberComparer.cs
│ │ ├── Example3/
│ │ │ ├── Example3Tests.cs
│ │ │ ├── Settings0.json
│ │ │ ├── Settings1.json
│ │ │ └── Settings2.json
│ │ ├── Example4/
│ │ │ ├── CustomFormulaItemsComparer.cs
│ │ │ ├── Example4Tests.cs
│ │ │ ├── Formula.cs
│ │ │ ├── FormulaItem.cs
│ │ │ └── MyComparersFactory.cs
│ │ ├── Example5/
│ │ │ ├── Error.cs
│ │ │ ├── Example5Tests.cs
│ │ │ └── IgnoreAttribute.cs
│ │ ├── ObjectsComparer.Examples.csproj
│ │ └── OutputHelper.cs
│ ├── ObjectsComparer.Tests/
│ │ ├── AbstractComparerGenericTests.cs
│ │ ├── AbstractComparerTests.cs
│ │ ├── ComparerNonGenericTests.cs
│ │ ├── ComparerOverridesCollectionTests.cs
│ │ ├── ComparerTests.cs
│ │ ├── Comparer_CompilerGeneratedObjectsTests.cs
│ │ ├── Comparer_DynamicObjectsTests.cs
│ │ ├── Comparer_ExpandoObjectsTests.cs
│ │ ├── Comparer_GenericEnumerableTests.cs
│ │ ├── Comparer_IPEndPointTests.cs
│ │ ├── Comparer_Issue24Tests.cs
│ │ ├── Comparer_MultidimensionalArraysTests.cs
│ │ ├── Comparer_NonGenericEnumerableTests.cs
│ │ ├── Comparer_OverridesTests.cs
│ │ ├── Comparer_StringBuilderTests.cs
│ │ ├── Comparer_UriTests.cs
│ │ ├── ComparersFactoryTests.cs
│ │ ├── ComparisonSettingsTests.cs
│ │ ├── CustomComparers/
│ │ │ ├── AbstractValueComparerTests.cs
│ │ │ ├── DynamicValueComparerTests.cs
│ │ │ ├── EnumerablesComparerGenericTests.cs
│ │ │ ├── EnumerablesComparerTests.cs
│ │ │ ├── HashSetsComparerTests.cs
│ │ │ └── TypesComparerTests.cs
│ │ ├── DifferenceTests.cs
│ │ ├── ObjectsComparer.Tests.csproj
│ │ ├── ObjectsComparer.Tests.csproj.DotSettings
│ │ ├── ParentInterfacePropertiesTests.cs
│ │ ├── TestClasses/
│ │ │ ├── A.cs
│ │ │ ├── B.cs
│ │ │ ├── Child.cs
│ │ │ ├── CollectionOfB.cs
│ │ │ ├── EnumerableImplementation.cs
│ │ │ ├── FlagsEnum.cs
│ │ │ ├── ITestInterface.cs
│ │ │ ├── InheritedFromB.cs
│ │ │ ├── MultidimensionalArrays.cs
│ │ │ ├── Parent.cs
│ │ │ ├── ParentChild.cs
│ │ │ ├── TestEnum.cs
│ │ │ ├── TestInterfaceImplementation1.cs
│ │ │ ├── TestInterfaceImplementation2.cs
│ │ │ └── TestStruct.cs
│ │ ├── Utils/
│ │ │ └── TypeExtensionsTests.cs
│ │ └── ValueComparers/
│ │ ├── DefaultValueComparerTests.cs
│ │ ├── DefaultValueValueComparerTests.cs
│ │ ├── DoNotCompareValueComparerTests.cs
│ │ ├── IgnoreCaseStringsValueComparerTests.cs
│ │ └── NulableStringsValueComparerTests.cs
│ └── ObjectsComparer.sln
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 Valerii Tereshchenko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: ObjectsComparer/ObjectsComparer/AbstractComparer.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
namespace ObjectsComparer
{
///
/// Implementation of which provides implementation of Compare methods.
///
public abstract class AbstractComparer: BaseComparer, IComparer
{
protected AbstractComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)
: base(settings, parentComparer, factory)
{
}
public abstract IEnumerable CalculateDifferences(Type type, object obj1, object obj2);
///
/// Calculates list of differences between objects.
///
/// Type.
/// Object 1.
/// Object 2.
/// List of differences between objects.
public IEnumerable CalculateDifferences(T obj1, T obj2)
{
return CalculateDifferences(typeof(T), obj1, obj2);
}
///
/// Compares objects.
///
/// Type.
/// Object 1.
/// Object 2.
/// List of differences.
/// True if objects are equal, otherwise false.
public bool Compare(Type type, object obj1, object obj2, out IEnumerable differences)
{
differences = CalculateDifferences(type, obj1, obj2);
return !differences.Any();
}
///
/// Compares objects.
///
/// Type.
/// Object 1.
/// Object 2.
/// True if objects are equal, otherwise false.
public bool Compare(Type type, object obj1, object obj2)
{
return !CalculateDifferences(type, obj1, obj2).Any();
}
///
/// Compares objects.
///
/// Type.
/// Object 1.
/// Object 2.
/// List of differences.
/// True if objects are equal, otherwise false.
public bool Compare(T obj1, T obj2, out IEnumerable differences)
{
return Compare(typeof(T), obj1, obj2, out differences);
}
///
/// Compares objects.
///
/// Type.
/// Object 1.
/// Object 2.
/// True if objects are equal, otherwise false.
public bool Compare(T obj1, T obj2)
{
return Compare(typeof(T), obj1, obj2);
}
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/AbstractComparer~1.cs
================================================
using System.Collections.Generic;
using System.Linq;
namespace ObjectsComparer
{
///
/// Implementation of which provides implementation of Compare methods.
///
public abstract class AbstractComparer: BaseComparer, IComparer
{
protected AbstractComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)
:base(settings, parentComparer, factory)
{
}
///
/// Compares objects.
///
/// Object 1.
/// Object 2.
/// List of differences.
/// True if objects are equal, otherwise false.
public bool Compare(T obj1, T obj2, out IEnumerable differences)
{
differences = CalculateDifferences(obj1, obj2);
return !differences.Any();
}
///
/// Compares objects.
///
/// Object 1.
/// Object 2.
/// True if objects are equal, otherwise false.
public bool Compare(T obj1, T obj2)
{
return !CalculateDifferences(obj1, obj2).Any();
}
///
/// Calculates list of differences between objects.
///
/// Object 1.
/// Object 2.
/// List of differences between objects.
public abstract IEnumerable CalculateDifferences(T obj1, T obj2);
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/Attributes/IgnoreInComparisonAttribute.cs
================================================
// ***********************************************************************
// Assembly : Objects Comparer
// Author : Ankur Kumar Gupta
// Created : 24-Feb-2022
// ***********************************************************************
namespace ObjectsComparer.Attributes
{
using System;
///
/// Class is used to specify whether the element on which it is applied will have comparison effect
///
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class IgnoreInComparisonAttribute : Attribute
{
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/BaseComparer.cs
================================================
using System;
using System.Linq.Expressions;
using System.Reflection;
using ObjectsComparer.Utils;
namespace ObjectsComparer
{
///
/// Provides base implementation to configure comparer.
///
public abstract class BaseComparer: IBaseComparer
{
///
/// Comparison Settings.
///
public ComparisonSettings Settings { get; }
///
/// Default
///
public IValueComparer DefaultValueComparer { get; private set; }
protected IComparersFactory Factory { get; }
internal ComparerOverridesCollection OverridesCollection { get; } = new ComparerOverridesCollection();
protected BaseComparer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory)
{
Factory = factory ?? new ComparersFactory();
Settings = settings ?? new ComparisonSettings();
DefaultValueComparer = new DefaultValueComparer();
// ReSharper disable once InvertIf
if (parentComparer != null)
{
DefaultValueComparer = parentComparer.DefaultValueComparer;
OverridesCollection.Merge(parentComparer.OverridesCollection);
}
}
///
/// Adds Comparer Override by Type.
///
/// Type.
/// Value Comparer.
/// Value Comparer will be used only if filter(memberInfo) == true. Null by default.
public void AddComparerOverride(Type type, IValueComparer valueComparer, Func filter = null)
{
OverridesCollection.AddComparer(type, valueComparer, filter);
}
///
/// Adds Comparer Override by member filter.
///
/// Value Comparer.
/// Value Comparer will be used only if filter(memberInfo) == true.
public void AddComparerOverride(IValueComparer valueComparer, Func filter)
{
OverridesCollection.AddComparer(valueComparer, filter);
}
///
/// Adds Comparer Override by Type.
///
/// Type.
/// Value Comparer.
/// Value Comparer will be used only if filter(memberInfo) == true. Null by default.
public void AddComparerOverride(IValueComparer valueComparer, Func filter = null)
{
AddComparerOverride(typeof(TType), valueComparer, filter);
}
///
/// Adds Comparer Override by Member.
///
/// Type of the member.
/// Lambda to get member.
/// Value Comparer.
public void AddComparerOverride(Expression> memberLambda, IValueComparer valueComparer)
{
OverridesCollection.AddComparer(PropertyHelper.GetMemberInfo(memberLambda), valueComparer);
}
///
/// Adds Comparer Override by Member.
///
/// Type of the member.
/// Lambda to get member.
/// Function to compare objects.
/// Function to convert objects to string.
public void AddComparerOverride(
Expression> memberLambda,
Func compareFunction,
Func toStringFunction)
{
OverridesCollection.AddComparer(
PropertyHelper.GetMemberInfo(memberLambda),
new DynamicValueComparer(
compareFunction,
toStringFunction));
}
///
/// Adds Comparer Override by Member.
///
/// Type of the member.
/// Lambda to get member.
/// Function to compare objects.
public void AddComparerOverride(
Expression> memberLambda,
Func compareFunction)
{
OverridesCollection.AddComparer(
PropertyHelper.GetMemberInfo(memberLambda),
new DynamicValueComparer(
compareFunction,
obj => obj?.ToString()));
}
///
/// Adds Comparer Override by Member.
///
/// Member Info.
/// Value Comparer.
public void AddComparerOverride(MemberInfo memberInfo, IValueComparer valueComparer)
{
OverridesCollection.AddComparer(memberInfo, valueComparer);
}
///
/// Adds Comparer Override by Member name.
///
/// Member Name.
/// Value Comparer.
/// Value Comparer will be used only if filter(memberInfo) == true. Null by default.
public void AddComparerOverride(string memberName, IValueComparer valueComparer, Func filter = null)
{
OverridesCollection.AddComparer(memberName, valueComparer, filter);
}
///
/// Sets .
///
/// Value Comparer.
public void SetDefaultComparer(IValueComparer valueComparer)
{
DefaultValueComparer = valueComparer ?? throw new ArgumentNullException(nameof(valueComparer));
}
///
/// Ignors comparison for Type.
///
/// Type.
public void IgnoreMember()
{
OverridesCollection.AddComparer(typeof(TType), DoNotCompareValueComparer.Instance);
}
///
/// Ignors comparison for Member.
///
/// Type of the member.
public void IgnoreMember(Expression> memberLambda)
{
OverridesCollection.AddComparer(
PropertyHelper.GetMemberInfo(memberLambda),
DoNotCompareValueComparer.Instance);
}
///
/// Ignors comparison for Member by Member name.
///
/// Member Name.
public void IgnoreMember(string memberName)
{
OverridesCollection.AddComparer(memberName, DoNotCompareValueComparer.Instance);
}
///
/// Ignors comparison by Member filter.
///
/// Member will be ignored if filter(memberInfo) == true.
public void IgnoreMember(Func filter)
{
OverridesCollection.AddComparer(DoNotCompareValueComparer.Instance, filter);
}
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/Comparer.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ObjectsComparer.Utils;
namespace ObjectsComparer
{
///
/// Compares objects.
///
public class Comparer : AbstractComparer
{
private static string CalculateDifferencesMethodName
{
// ReSharper disable once IteratorMethodResultIsIgnored
get { return MemberInfoExtensions.GetMethodName>(x => x.CalculateDifferences(null, null)); }
}
///
/// Initializes a new instance of the class.
///
/// Comparison Settings.
/// Parent Comparer. Is used to copy DefaultValueComparer and Overrides. Null by default.
/// Factory to create comparers in case of some members of the objects will need it.
public Comparer(ComparisonSettings settings = null, BaseComparer parentComparer = null, IComparersFactory factory = null) : base(settings, parentComparer, factory)
{
}
///
/// Calculates list of differences between objects.
///
/// Type.
/// Object 1.
/// Object 2.
/// List of differences between objects.
public override IEnumerable CalculateDifferences(Type type, object obj1, object obj2)
{
var objectsComparerMethod = typeof(IComparersFactory).GetTypeInfo().GetMethods().First(m => m.IsGenericMethod);
var objectsComparerGenericMethod = objectsComparerMethod.MakeGenericMethod(type);
var comparer = objectsComparerGenericMethod.Invoke(Factory, new object[] { Settings, this });
var genericType = typeof(IComparer<>).MakeGenericType(type);
var method = genericType.GetTypeInfo().GetMethod(CalculateDifferencesMethodName, new[] { type, type });
// ReSharper disable once PossibleNullReferenceException
return (IEnumerable)method.Invoke(comparer, new[] { obj1, obj2 });
}
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/ComparerOverridesCollection.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ObjectsComparer.Exceptions;
using ObjectsComparer.Utils;
namespace ObjectsComparer
{
internal class ComparerOverridesCollection
{
private class ValueComparerWithFilter
{
public IValueComparer ValueComparer { get; }
public Func Filter { get; }
public ValueComparerWithFilter(IValueComparer valueComparer, Func filter)
{
Filter = filter;
ValueComparer = valueComparer;
}
}
private readonly List, IValueComparer>> _overridesByMemberFilter = new List, IValueComparer>>();
private readonly Dictionary _overridesByMember = new Dictionary();
private readonly Dictionary> _overridesByType = new Dictionary>();
private readonly Dictionary> _overridesByName = new Dictionary>();
public void AddComparer(MemberInfo memberInfo, IValueComparer valueComparer)
{
if (memberInfo == null)
{
throw new ArgumentNullException(nameof(memberInfo));
}
if (_overridesByMember.ContainsKey(memberInfo))
{
throw new ValueComparerExistsException(memberInfo);
}
_overridesByMember[memberInfo] = valueComparer ?? throw new ArgumentNullException(nameof(valueComparer));
}
public void AddComparer(IValueComparer valueComparer, Func filter)
{
if (filter == null)
{
throw new ArgumentNullException(nameof(filter));
}
_overridesByMemberFilter.Add(new Tuple, IValueComparer>(filter, valueComparer));
}
public void AddComparer(Type type, IValueComparer valueComparer,
Func filter = null)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (valueComparer == null)
{
throw new ArgumentNullException(nameof(valueComparer));
}
if (!_overridesByType.ContainsKey(type))
{
_overridesByType[type] = new List();
}
if (!_overridesByType[type].Any(comparer => comparer.ValueComparer == valueComparer && comparer.Filter == filter))
{
_overridesByType[type].Add(new ValueComparerWithFilter(valueComparer, filter));
}
}
public void AddComparer(string memberName, IValueComparer valueComparer,
Func filter = null)
{
if (string.IsNullOrWhiteSpace(memberName))
{
throw new ArgumentException($"{nameof(memberName)} cannot be null or empty");
}
if (valueComparer == null)
{
throw new ArgumentNullException(nameof(valueComparer));
}
if (!_overridesByName.ContainsKey(memberName))
{
_overridesByName[memberName] = new List();
}
_overridesByName[memberName].Add(new ValueComparerWithFilter(valueComparer, filter));
}
public void Merge(ComparerOverridesCollection collection)
{
foreach (var overridePair in collection._overridesByMember)
{
AddComparer(overridePair.Key, overridePair.Value);
}
foreach (var overrideCollection in collection._overridesByType)
{
foreach (var overridePair in overrideCollection.Value)
{
AddComparer(overrideCollection.Key, overridePair.ValueComparer, overridePair.Filter);
}
}
foreach (var overrideCollection in collection._overridesByName)
{
foreach (var overridePair in overrideCollection.Value)
{
AddComparer(overrideCollection.Key, overridePair.ValueComparer, overridePair.Filter);
}
}
foreach (var memberFilterOverride in collection._overridesByMemberFilter)
{
AddComparer(memberFilterOverride.Item2, memberFilterOverride.Item1);
}
}
public IValueComparer GetComparer(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (!_overridesByType.TryGetValue(type, out var overridesByType))
{
return null;
}
overridesByType = overridesByType.Where(o => o.Filter == null).ToList();
if (overridesByType.Count > 1)
{
throw new AmbiguousComparerOverrideResolutionException(type);
}
return overridesByType.Count == 1 ? overridesByType[0].ValueComparer : null;
}
public IValueComparer GetComparer(MemberInfo memberInfo)
{
if (memberInfo == null)
{
throw new ArgumentNullException(nameof(memberInfo));
}
if (_overridesByMember.TryGetValue(memberInfo, out var overrideByMemberInfo))
{
return overrideByMemberInfo;
}
var memberFilterOverride = _overridesByMemberFilter.Find(t => t.Item1(memberInfo))?.Item2;
if (memberFilterOverride != null)
{
return memberFilterOverride;
}
if (_overridesByName.TryGetValue(memberInfo.Name, out var overridesByName))
{
overridesByName = overridesByName.Where(o => o.Filter == null || o.Filter(memberInfo)).ToList();
if (overridesByName.Count > 1)
{
throw new AmbiguousComparerOverrideResolutionException(memberInfo);
}
if (overridesByName.Count == 1)
{
return overridesByName[0].ValueComparer;
}
}
if (_overridesByType.TryGetValue(memberInfo.GetMemberType(), out var overridesByType))
{
overridesByType = overridesByType.Where(o => o.Filter == null || o.Filter(memberInfo)).ToList();
if (overridesByType.Count > 1)
{
throw new AmbiguousComparerOverrideResolutionException(memberInfo);
}
if (overridesByType.Count == 1)
{
return overridesByType[0].ValueComparer;
}
}
return null;
}
public IValueComparer GetComparer(string memberName)
{
if (string.IsNullOrWhiteSpace(memberName))
{
throw new ArgumentNullException(nameof(memberName));
}
if (!_overridesByName.TryGetValue(memberName, out var overridesByName))
{
return null;
}
overridesByName = overridesByName.Where(o => o.Filter == null).ToList();
if (overridesByName.Count > 1)
{
throw new AmbiguousComparerOverrideResolutionException(memberName);
}
return overridesByName.Count == 1 ? overridesByName[0].ValueComparer : null;
}
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/ComparersFactory.cs
================================================
using System;
namespace ObjectsComparer
{
///
/// Implements Comparers Factory.
///
public class ComparersFactory : IComparersFactory
{
///
/// Creates type specific comparer.
///
/// Type.
/// Comparison Settings. Null by default.
/// Parent comparer. Null by default.
/// Instance of .
public virtual IComparer GetObjectsComparer(ComparisonSettings settings = null, BaseComparer parentComparer = null)
{
return new Comparer(settings, parentComparer, this);
}
///
/// Creates type specific comparer.
///
/// Type.
/// Comparison Settings. Null by default.
/// Parent comparer. Null by default.
/// Instance of .
public IComparer GetObjectsComparer(Type type, ComparisonSettings settings = null, BaseComparer parentComparer = null)
{
return new Comparer(settings, parentComparer, this);
}
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/Comparer~1.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using ObjectsComparer.Utils;
using ObjectsComparer.Attributes;
namespace ObjectsComparer
{
///
/// Compares objects of type .
///
public class Comparer : AbstractComparer
{
private readonly List _members;
private readonly List _conditionalComparers;
///
/// Initializes a new instance of the class.
///
/// Comparison Settings.
/// Parent Comparer. Is used to copy DefaultValueComparer and Overrides. Null by default.
/// Factory to create comparers in case of some members of the objects will need it.
public Comparer(ComparisonSettings settings = null, BaseComparer parentComparer = null, IComparersFactory factory = null)
: base(settings, parentComparer, factory)
{
var properties = GetProperties(typeof(T), new List());
var fields = typeof(T).GetTypeInfo().GetFields().Where(f =>
f.IsPublic
&& !f.IsStatic
&& !f.GetCustomAttributes(true).Any(c=> c is IgnoreInComparisonAttribute)).ToList();
_members = properties.Union(fields.Cast()).ToList();
_conditionalComparers = new List
{
new MultidimensionalArraysComparer(Settings, this, Factory),
new ExpandoObjectComparer(Settings, this, Factory),
new DynamicObjectComparer(Settings, this, Factory),
new CompilerGeneratedObjectComparer(Settings, this, Factory),
new HashSetsComparer(Settings, this, Factory),
new GenericEnumerablesComparer(Settings, this, Factory),
new EnumerablesComparer(Settings, this, Factory),
new TypesComparer(Settings, this, Factory)
};
// Additional value comparers
AddComparerOverride(new ToStringComparer());
AddComparerOverride(UriComparer.Instance);
}
///
/// Calculates list of differences between objects.
///
/// Object 1.
/// Object 2.
/// List of differences between objects.
public override IEnumerable CalculateDifferences(T obj1, T obj2)
{
return CalculateDifferences(obj1, obj2, null);
}
internal IEnumerable CalculateDifferences(T obj1, T obj2, MemberInfo memberInfo)
{
var comparer = memberInfo != null
? OverridesCollection.GetComparer(memberInfo)
: OverridesCollection.GetComparer(typeof(T));
if (typeof(T).IsComparable() ||
comparer != null)
{
comparer = comparer ?? DefaultValueComparer;
if (!comparer.Compare(obj1, obj2, Settings))
{
yield return
new Difference(string.Empty, comparer.ToString(obj1),
comparer.ToString(obj2));
}
yield break;
}
var conditionalComparer = _conditionalComparers.FirstOrDefault(c => c.IsMatch(typeof(T), obj1, obj2));
if (conditionalComparer != null)
{
foreach (var difference in conditionalComparer.CalculateDifferences(typeof(T), obj1, obj2))
{
yield return difference;
}
if (conditionalComparer.IsStopComparison(typeof(T), obj1, obj2))
{
yield break;
}
}
if (obj1 == null || obj2 == null)
{
if (!DefaultValueComparer.Compare(obj1, obj2, Settings))
{
yield return new Difference(string.Empty, DefaultValueComparer.ToString(obj1), DefaultValueComparer.ToString(obj2));
}
yield break;
}
if (!Settings.RecursiveComparison)
{
yield break;
}
foreach (var member in _members)
{
if (member.GetCustomAttributes(true).Any(c => c is IgnoreInComparisonAttribute))
{
continue;
}
var value1 = member.GetMemberValue(obj1);
var value2 = member.GetMemberValue(obj2);
var type = member.GetMemberType();
if (conditionalComparer != null && conditionalComparer.SkipMember(typeof(T), member))
{
continue;
}
var valueComparer = DefaultValueComparer;
var hasCustomComparer = false;
var comparerOverride = OverridesCollection.GetComparer(member);
if (comparerOverride != null)
{
valueComparer = comparerOverride;
hasCustomComparer = true;
}
if (!hasCustomComparer
&& !type.IsComparable())
{
var objectDataComparer = Factory.GetObjectsComparer(type, Settings, this);
foreach (var failure in objectDataComparer.CalculateDifferences(type, value1, value2))
{
yield return failure.InsertPath(member.Name);
}
continue;
}
if (!valueComparer.Compare(value1, value2, Settings))
{
yield return new Difference(member.Name, valueComparer.ToString(value1), valueComparer.ToString(value2));
}
}
}
private List GetProperties(Type type, List processedTypes)
{
var properties = type.GetTypeInfo().GetProperties().Where(p =>
p.CanRead
&& p.GetGetMethod(true).IsPublic
&& p.GetGetMethod(true).GetParameters().Length == 0
&& !p.GetCustomAttributes(true).Any(c=> c is IgnoreInComparisonAttribute)
&& !p.GetGetMethod(true).IsStatic).ToList();
processedTypes.Add(type);
if (type.GetTypeInfo().IsInterface)
{
foreach (var parrentInterface in type.GetTypeInfo().GetInterfaces())
{
if (processedTypes.Contains(parrentInterface))
{
continue;
}
properties = properties
.Union(GetProperties(parrentInterface, processedTypes))
.Distinct()
.ToList();
}
}
return properties;
}
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/ComparisonSettings.cs
================================================
using System;
using System.Collections.Generic;
namespace ObjectsComparer
{
///
/// Configuration for Objects Comparer.
///
public class ComparisonSettings
{
///
/// If true, all members which are not primitive types, do not have custom comparison rule and
/// do not implement will be compared as separate objects using the same rules as current objects. True by default.
///
public bool RecursiveComparison { get; set; }
///
/// If true, empty and null values will be considered as equal values. False by default.
///
public bool EmptyAndNullEnumerablesEqual { get; set; }
///
/// If true and member does not exists, objects comparer will consider that this member is equal to default value of opposite member type.
/// Applicable for dynamic types comparison only. False by default.
///
public bool UseDefaultIfMemberNotExist { get; set; }
private readonly Dictionary, object> _settings = new Dictionary, object>();
///
/// Initializes a new instance of the class.
///
public ComparisonSettings()
{
RecursiveComparison = true;
EmptyAndNullEnumerablesEqual = false;
UseDefaultIfMemberNotExist = false;
}
///
/// Sets value of custom setting. Could be used to pass parameters to custom value comparers.
///
/// Setting Type.
/// Setting Value.
/// Setting Key.
public void SetCustomSetting(T value, string key = null)
{
var dictionaryKey = new Tuple(typeof(T), key);
_settings[dictionaryKey] = value;
}
///
/// Gets value of custom setting. Could be used in custom value comparers.
///
/// Setting Type.
/// Setting Key.
/// Setting Value.
public T GetCustomSetting(string key = null)
{
var dictionaryKey = new Tuple(typeof(T), key);
if (_settings.TryGetValue(dictionaryKey, out var value))
{
return (T) value;
}
throw new KeyNotFoundException();
}
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/CustomComparers/AbstractDynamicObjectsComprer.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ObjectsComparer.Utils;
namespace ObjectsComparer
{
internal abstract class AbstractDynamicObjectsComprer: AbstractComparer, IComparerWithCondition
{
protected AbstractDynamicObjectsComprer(ComparisonSettings settings, BaseComparer parentComparer, IComparersFactory factory) : base(settings, parentComparer, factory)
{
}
public override IEnumerable CalculateDifferences(Type type, object obj1, object obj2)
{
var castedObject1 = (T)obj1;
var castedObject2 = (T)obj2;
var propertyKeys1 = GetProperties(castedObject1);
var propertyKeys2 = GetProperties(castedObject2);
var propertyKeys = propertyKeys1.Union(propertyKeys2);
foreach (var propertyKey in propertyKeys)
{
var existsInObject1 = propertyKeys1.Contains(propertyKey);
var existsInObject2 = propertyKeys2.Contains(propertyKey);
object value1 = null;
if (existsInObject1)
{
TryGetMemberValue(castedObject1, propertyKey, out value1);
}
object value2 = null;
if (existsInObject2)
{
TryGetMemberValue(castedObject2, propertyKey, out value2);
}
var propertyType = (value1 ?? value2)?.GetType() ?? typeof(object);
var customComparer = OverridesCollection.GetComparer(propertyType) ??
OverridesCollection.GetComparer(propertyKey);
var valueComparer = customComparer ?? DefaultValueComparer;
if (Settings.UseDefaultIfMemberNotExist)
{
if (!existsInObject1)
{
value1 = propertyType.GetDefaultValue();
}
if (!existsInObject2)
{
value2 = propertyType.GetDefaultValue();
}
}
if (!Settings.UseDefaultIfMemberNotExist)
{
if (!existsInObject1)
{
yield return new Difference(propertyKey, string.Empty, valueComparer.ToString(value2),
DifferenceTypes.MissedMemberInFirstObject);
continue;
}
if (!existsInObject2)
{
yield return new Difference(propertyKey, valueComparer.ToString(value1), string.Empty,
DifferenceTypes.MissedMemberInSecondObject);
continue;
}
}
if (value1 != null && value2 != null && value1.GetType() != value2.GetType())
{
var valueComparer2 = OverridesCollection.GetComparer(value2.GetType()) ??
OverridesCollection.GetComparer(propertyKey) ??
DefaultValueComparer;
yield return new Difference(propertyKey, valueComparer.ToString(value1), valueComparer2.ToString(value2),
DifferenceTypes.TypeMismatch);
continue;
}
//null cannot be casted to ValueType
if (value1 == null && value2 != null && value2.GetType().GetTypeInfo().IsValueType ||
value2 == null && value1 != null && value1.GetType().GetTypeInfo().IsValueType)
{
var valueComparer2 = value2 != null ?
OverridesCollection.GetComparer(value2.GetType()) ?? OverridesCollection.GetComparer(propertyKey) ?? DefaultValueComparer :
DefaultValueComparer;
yield return new Difference(propertyKey, valueComparer.ToString(value1), valueComparer2.ToString(value2),
DifferenceTypes.TypeMismatch);
continue;
}
if (customComparer != null)
{
if (!customComparer.Compare(value1, value2, Settings))
{
yield return new Difference(propertyKey, customComparer.ToString(value1), customComparer.ToString(value2));
}
continue;
}
var comparer = Factory.GetObjectsComparer(propertyType, Settings, this);
foreach (var failure in comparer.CalculateDifferences(propertyType, value1, value2))
{
yield return failure.InsertPath(propertyKey);
}
}
}
public abstract bool IsMatch(Type type, object obj1, object obj2);
public abstract bool IsStopComparison(Type type, object obj1, object obj2);
public abstract bool SkipMember(Type type, MemberInfo member);
protected abstract IList GetProperties(T obj);
protected abstract bool TryGetMemberValue(T obj, string propertyName, out object value);
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/CustomComparers/AbstractEnumerablesComparer.cs
================================================
using System;
using System.Collections.Generic;
using System.Reflection;
using ObjectsComparer.Utils;
namespace ObjectsComparer
{
internal abstract class AbstractEnumerablesComparer: AbstractComparer, IComparerWithCondition
{
protected AbstractEnumerablesComparer(ComparisonSettings settings, BaseComparer parentComparer,
IComparersFactory factory)
: base(settings, parentComparer, factory)
{
}
public bool IsStopComparison(Type type, object obj1, object obj2)
{
return Settings.EmptyAndNullEnumerablesEqual && (obj1 == null || obj2 == null);
}
public virtual bool SkipMember(Type type, MemberInfo member)
{
if (type.InheritsFrom(typeof(Array)))
{
if (member.Name == "LongLength")
{
return true;
}
}
if (type.InheritsFrom(typeof(List<>)))
{
if (member.Name == PropertyHelper.GetMemberInfo(() => new List().Capacity).Name)
{
return true;
}
}
return false;
}
public abstract override IEnumerable CalculateDifferences(Type type, object obj1, object obj2);
public abstract bool IsMatch(Type type, object obj1, object obj2);
}
}
================================================
FILE: ObjectsComparer/ObjectsComparer/CustomComparers/CompilerGeneratedObjectComparer.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace ObjectsComparer
{
internal class CompilerGeneratedObjectComparer : AbstractDynamicObjectsComprer