Skip to content

Commit

Permalink
Merge pull request #279 from Mch-Kuzyk/master
Browse files Browse the repository at this point in the history
Enchantment for issue #277 (ComparisonConfig.CompareBackingFields)
  • Loading branch information
GregFinzer authored Jan 28, 2023
2 parents b5e85d2 + 76cc240 commit e352762
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 24 deletions.
39 changes: 39 additions & 0 deletions Compare-NET-Objects-Tests/CompareClassTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using KellermanSoftware.CompareNetObjects;
using KellermanSoftware.CompareNetObjectsTests.TestClasses;
using NUnit.Framework;
Expand Down Expand Up @@ -216,6 +217,44 @@ public void PrivateFieldInBaseClassNegative()
}
#endregion

#region Backing Field Tests
[Test]
public void BackingFieldPositive()
{
RecipeDetail detail1 = new RecipeDetail(true, "Toffee");
detail1.Ingredient = "Crunchy Chocolate";

RecipeDetail detail2 = new RecipeDetail(true, "Crunchy Frogs");
detail2.Ingredient = "Crunchy Chocolate";

_compare.Config.ComparePrivateFields = true;
_compare.Config.CompareBackingFields = false;
var result = _compare.Compare(detail1, detail2);
Assert.IsTrue(result.AreEqual);
Assert.IsTrue(result.Differences.Count == 0);
_compare.Config.ComparePrivateFields = false;
_compare.Config.CompareBackingFields = true;
}

[Test]
public void BackingFieldNegative()
{
RecipeDetail detail1 = new RecipeDetail(true, "Toffee");
detail1.Ingredient = "Crunchy Chocolate";

RecipeDetail detail2 = new RecipeDetail(true, "Crunchy Frogs");
detail2.Ingredient = "Crunchy Chocolate";

_compare.Config.ComparePrivateFields = true;
_compare.Config.CompareBackingFields = true;
var result = _compare.Compare(detail1, detail2);
Assert.IsFalse(result.AreEqual);
Assert.IsTrue(result.Differences.Any(dif => dif.PropertyName == "<SecretIngredient>k__BackingField"));
_compare.Config.ComparePrivateFields = false;
_compare.Config.CompareBackingFields = true;
}
#endregion

#endif

#region InterfaceMembers
Expand Down
59 changes: 35 additions & 24 deletions Compare-NET-Objects/Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,35 +68,46 @@ public static IEnumerable<FieldInfo> GetFieldInfo(ComparisonConfig config, Type
FieldInfo[] currentFields;

#if !NETSTANDARD1_3
//All the implementation examples that I have seen for dynamic objects use private fields or properties
if (( config.ComparePrivateFields || isDynamicType) && !config.CompareStaticFields)
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
if (config.ComparePrivateFields || isDynamicType)
{
List<FieldInfo> list = new List<FieldInfo>();
Type t = type;
do
{
list.AddRange(t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
t = t.BaseType;
} while (t != null);
currentFields = list.ToArray();
flags |= BindingFlags.NonPublic;
}
else if ((config.ComparePrivateFields || isDynamicType) && config.CompareStaticFields)
if (config.CompareStaticFields)
{
List<FieldInfo> list = new List<FieldInfo>();
Type t = type;
do
{
list.AddRange(
t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Static));
t = t.BaseType;
} while (t != null);
currentFields = list.ToArray();
flags |= BindingFlags.Static;
}
else
#endif
currentFields = type.GetFields(); //Default is public instance and static

Func<FieldInfo, bool> fieldIsBacking = field => field.Name.Contains("k__BackingField");
List<FieldInfo> list = new List<FieldInfo>();
Type t = type;
do
{
foreach (var field in t.GetFields(flags))
{
if (!config.ComparePrivateFields)
{
list.Add(field);
}
else if (config.ComparePrivateFields && !config.CompareBackingFields && !fieldIsBacking(field))
{
list.Add(field);
}
else if (config.ComparePrivateFields && config.CompareBackingFields)
{
list.Add(field);
}
else if (isDynamicType)
{
list.Add(field);
}
}
t = t.BaseType;
} while (t != null);
currentFields = list.ToArray();
#else
currentFields = type.GetFields(); //Default is public instance and static
#endif
if (config.Caching)
_fieldCache.Add(type, currentFields);

Expand Down
11 changes: 11 additions & 0 deletions Compare-NET-Objects/ComparisonConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,16 @@ public void CustomPropertyComparer<TClass>(Expression<Func<TClass, object>> cust
public bool ComparePrivateFields { get; set; }
#endif

#if !NETSTANDARD1_3
/// <summary>
/// If true and <see cref="ComparePrivateFields"/> true, then backing fields will be compared. The default is true. Silverlight and WinRT restricts access to private variables.
/// </summary>
#if !NETSTANDARD
[DataMember]
#endif
public bool CompareBackingFields { get; set; }
#endif

/// <summary>
/// If true, static properties will be compared. The default is true.
/// </summary>
Expand Down Expand Up @@ -643,6 +653,7 @@ public void Reset()
#if !NETSTANDARD1_3
ComparePrivateProperties = false;
ComparePrivateFields = false;
CompareBackingFields = true;
#endif
CustomPropertyComparers = new Dictionary<string, BaseTypeComparer>();
CompareChildren = true;
Expand Down

0 comments on commit e352762

Please sign in to comment.