Simplifying a boolean logic - c#

I have a function which should return true if all items pass the test. If only one item fails, then the function should return false. If there's no item in the collection the function should return false. Here is the code:
private bool TestAll()
{
bool finalResult = false;
bool itemResult = false;
foreach (var item in Items)
{
itemResult = Test(item);
if (!finalResult && itemResult)
finalResult = true;
else if (finalResult && !itemResult)
finalResult = false;
}
return finalResult;
}
How can I simplify the logic into one if statement using just one bool variable?

You can use the IEnumerable.All extension method to test all items, which fails on the first instance of the Test method failing.
private bool TestAll()
{
return Items.All(Test);
}
If you still need all items to be tested, you could probably use the AND assignment operator:
if (!Items.Any()) return false;
bool result = true;
foreach (var item in Items)
{
result &= Test(item);
}
return result;

If all the tests need to run, you can do it like this without LINQ:
private bool TestAll()
{
var allTestsPassed = true;
foreach (var item in Items)
{
allTestsPassed = Test(item) && allTestsPassed;
}
return allTestsPassed;
}
You can do it like this with LINQ:
private bool TestAll()
{
return Items.Count(Test) == Items.Count();
}
Update: returning false if there are no tests to run
private bool TestAllWithoutLinq()
{
if (Items.Count == 0) { // or something equivalent
return false;
}
var allTestsPassed = true;
foreach (var item in Items)
{
allTestsPassed = Test(item) && allTestsPassed;
}
return allTestsPassed;
}
private bool TestAllWithLinq()
{
return Items.Any() && Items.Count(Test) == Items.Count();
}

I realize this has been answered, and the shortest answer is the LINQ answer. Similar to others, but it requires a split second of thought:
private bool TestAll()
{
var passed = true;
foreach (var item in Items)
{
if ( ! Test(item))
{
passed = false;
}
}
return passed && Items.Count != 0;
}

Seems like an odd requirement that it should return false if only one item fails. Am I reading the question correctly? If so you can use the following
private bool TestAll() {
int failCount = 0;
foreach (var item in Items) {
if (!Test(item)) {
failCount++;
}
}
return failCount != 1;
}

private bool TestAll()
{
foreach (var item in Items)
{
if(!(Test(item)) || Items.Count == 0)
{
return false;
}
}
return true;
}

This will run all tests, return false if there are no tests, and return true only if all the tests pass:
private bool TestAll() {
if (Items.Count == 0) return false;
bool passed = true;
foreach(var item in Items) {
if (!Test(item))
passed = false;
}
return passed;
}

Related

How to filter ObservableCollection on multplie values

I have an observable collection.
The class of this collection has several fields which in turn can have server values.
I wish my user to be able to search on numerous fields and values.
I have got as far as using an ICollection but all the examples I have seen is based on searching on 1 field.
This is my class:
public class Tasks
{
private string JobPriority;
private string JobLabel;
private string JobType;
private string UserRef;
}
JobPriority can be 'High', 'Low'
JobLabel can be 'Apple', 'Android', 'Microsoft'
JobType can be 'Internal', 'Private', 'Public'
So, I instantiate my collection as such:
public ObservableCollection<KanbanModelExtra> Tasks
{
get
{
if (_tasks == null)
{
TaskView = CollectionViewSource.GetDefaultView(_tasks);
TaskView.Filter = TaskFilter;
}
return _tasks;
}
set
{
_tasks = value;
}
}
public ICollectionView TaskView { get; private set; }
and set my filter as such:
private bool TaskFilter(
object item )
{
var task = item as KanbanModelExtra;
if ( task == null )
{
return false;
}
foreach ( var searchItem1 in JobPrioritiesToSearchOn )
{
if ( task.JobPriority == searchItem1 )
{
foreach ( var searchItem2 in JobLabelsToSearchOn )
{
if ( task.JobLabel == searchItem2)
{
foreach (var searchItem3 in JobTypesToSearchOn)
{
if (task.JobType == searchItem3)
{
if ( SearchByCurrentUser )
{
if ( task.UserRef == HeartBeat.CurrentUser.UserRef )
{
return true;
}
}
else
{
return true;
}
}
}
}
}
}
}
return false;
}
but this seems 'heavy' and ugly. Also, every change will refresh the collection. Where as I would lie to fresh the collection after all criteria has been choosen.
Is there a better way?
Chain the tests together.
var result = JobPrioritiesToSearchOn.Any(x => x == task.JobPriority);
result = result && JobLabelsToSearchOn.Any(x => x == task.JobLabel);
result = result && JobTypesToSearchOn.Any(x => x == task.JobType);
if (SearchByCurrentUser)
{
result = result && task.UserRef == HeartBeat.CurrentUser.UserRef;
}
return result;
Any() will return true if any of the collection match the filter passed in. In plain English, the first line evaluates to true if any of the JobPrioritiesToSearchOn items are equal to task.JobPriority.
foreach madness can be replaced by this expression:
return ((SearchByCurrentUser && task.UserRef == HeartBeat.CurrentUser.UserRef) || !SearchByCurrentUser) &&
JobPrioritiesToSearchOn.Contains(task.JobPriority) &&
JobLabelsToSearchOn.Contains(task.JobLabel) &&
JobTypesToSearchOn.Contains(task.JobType));

Finding all references to a method called by using reflection in Visual Studio

I have a class that implements ISupportIncrementalLoading interface. In this interface I am using reflection in order to get data from the data source. As I am passing method names as a string when I use "Find All References" in Visual Studio, it cannot find these classes.
This may cause problems if I change the signature of my method as I will not get any compile time errors, instead I will get a runtime error.
Is there a way to pass the name of the method that will let visual studio to accept it as a reference to the method.
This is my IncrementalCollection Class.
public class IncrementalCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
private bool hasMoreItems;
private int currentPage;
private string datasourceClass;
private string datasourceMethod;
private List<Object> parameters;
public IncrementalCollection(string datasourceClass, string datasourceMethod, List<Object> parameters)
{
this.datasourceClass = datasourceClass;
this.datasourceMethod = datasourceMethod;
this.parameters = parameters;
this.hasMoreItems = true;
}
public void ResetCollection(List<Object> parameters)
{
this.parameters = parameters;
currentPage = 0;
this.Clear();
}
public bool HasMoreItems
{
get { return hasMoreItems; }
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(
async () =>
{
uint resultCount = 0;
List<Object> modifiedParameters = new List<object>(this.parameters);
modifiedParameters.Add(++this.currentPage);
Type type = Type.GetType(this.datasourceClass);
MethodInfo method = type.GetTypeInfo().GetDeclaredMethod(this.datasourceMethod);
IList<T> result = await (Task<IList<T>>)method.Invoke(Activator.CreateInstance(type, null), modifiedParameters.ToArray());
if (result == null || result.Count == 0)
{
hasMoreItems = false;
}
else
{
resultCount = (uint)result.Count;
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
foreach (T item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
This is how I initialise IncrementalCollection, here I want to pass the name of the method by referencing it somehow.
List<Object> parameters = new List<Object>();
parameters.Add(url);
parameters.Add(null);
parameters.Add(null);
IncrementalCollection<User> _users = new IncrementalCollection<User>(_dataService.GetType().FullName, "GetUserList", parameters);
Thanks for your helps in advance.
You can just use the basic Func-style delegates. In your case, the consumer is passing in a delegate that takes one additional parameter:
var _users = new IncrementalCollection<User>(page => new DataService().GetUserList(uri, null, null, page));
And your IncrementalCollection is modified to use Func:
public class IncrementalCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
private bool hasMoreItems;
private int currentPage;
private Func<int, Task<IList<T>>> func;
public IncrementalCollection(Func<int, Task<IList<T>>> func)
{
this.func = func;
this.hasMoreItems = true;
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(async () =>
{
uint resultCount = 0;
var result = await func(++this.currentPage);
if (result == null || result.Count == 0)
{
hasMoreItems = false;
}
else
{
resultCount = (uint)result.Count;
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
foreach (T item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
However, I can't just let that dispatcher code go, and I doubt the Task.Run is necessary, since this appears to be I/O-based. It's better to write it more naturally with async/await:
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
return DoLoadMoreItemsAsync(count).AsAsyncOperation();
}
private async Task<LoadMoreItemsResult> DoLoadMoreItemsAsync(uint count)
{
var result = await func(++this.currentPage);
if (result == null || result.Count == 0)
{
hasMoreItems = false;
}
else
{
foreach (T item in result)
this.Add(item);
}
return new LoadMoreItemsResult() { Count = result == null ? 0 : result.Count };
}

How optimize by lambda expression

I have a very similar function is only one previous report and the other future, how can I optimize and write beautiful?
public bool AnyPreviousReportByGroup(int groupID)
{
if(this.GroupID == groupID)
{
return true;
}
else
{
return PreviousReport.AnyPreviousReportByGroup(groupID);
}
}
public bool AnyNextReportByGroup(int groupID)
{
if (this.GroupID == groupID)
{
return true;
}
else
{
return NextReport.AnyNextReportByGroup(groupID);
}
}
The following code is a conciser way of achieving the same thing:
public bool AnyPreviousReportByGroup(int groupID)
{
return this.GroupID == groupID ||
this.PreviousReport != null &&
this.PreviousReport.AnyPreviousReportByGroup(groupID);
}
If you really want to use lambda expressions, here's a possible way:
public bool AnyReportByGroup(int groupID, Func<Report, Report> getOtherReport)
{
if (this.GroupID == groupID)
return true;
Report other = getOtherReport(this);
return other != null &&
other.AnyReportByGroup(groupID, getOtherReport);
}
You could then call this helper method using lambda expressions:
bool anyPrevious = this.AnyReportByGroup(groupID, report => report.PreviousReport);
bool anyNext = this.AnyReportByGroup(groupID, report => report.NextReport);
private bool AnyReportByGroup(int groupID, Func<int, bool> callback)
{
if (this.GroupID == groupID)
{
return true;
}
else
{
return callback(groupID);
}
}
public bool AnyPreviousReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, gid => PreviousReport.AnyPreviousReportByGroup(gid));
}
public bool AnyNextReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, gid => NextReport.AnyNextReportByGroup(gid));
}
But, I hope, that these methods are just a sample, and in your real code they're more complex.
Otherwise, I can't understand, what do you try to optimize.
May be like this
public enum ReportType{ Next, Previous }
public bool AnyReportByGroup(int groupID, ReportType type)
{
if(this.GroupID == groupID)
return true;
else
{
switch(type)
{
case ReportType.Next:
return NextReport.AnyNextReportByGroup(groupID);
case ReportType.Previous:
return NextReport.AnyPreviousReportByGroup(groupID);
}
return false;
}
}
Here's a non-recursive solution, assuming that you want to return false when you run out of reports:
public bool AnyPreviousReportByGroup(int groupID)
{
return GetEventualValue(groupID, r => r.PreviousReport);
}
public bool AnyNextReportByGroup(int groupID)
{
return GetEventualValue(groupID, r => r.NextReport);
}
public bool GetEventualValue(int groupID, Func<Report, Report> nextReport)
{
Report report = this;
while (report != null && report.GroupID != groupID)
{
report = nextReport(report);
}
return report != null;
}
Standard way to traverse linked lists is like so:
public bool AnyPreviousReportByGroup(int groupID)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = item.PreviousReport;
} while (item != null);
return false;
}
public bool AnyNextReportByGroup(int groupID)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = item.NextReport;
} while (item != null);
return false;
}
This has a benefit of not creating potentially massive call stacks the way a recusive approach would.
This also fixes your code, where it never returned false, it would just NPE.
Now we can refactor as you requested:
private bool AnyReportByGroup(int groupID, bool searchForward)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = searchForward ? item.NextReport : item.PreviousReport;
} while (item != null);
return false;
}
public bool AnyPreviousReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, false);
}
public bool AnyNextReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, true);
}

Is there a way to collapse a subtree in a TreeView?

I am trying to add in functionality to my TreeView wherein a user can have all of the nodes expand and collapse at the click of a button. Expand works well and fine enough using ExpandSubTree. For whatever reason, there is no CollapseSubTree function. How can I successfully accomplish this task?
Here is my current function:
private void expand_collapse_children(TreeViewItem tvi, bool expand)
{
if (tvi.Items.Count > 0)
{
foreach (TreeViewItem item in tvi.Items)
{
if (expand)
{
item.ExpandSubtree();
}
else
{
expand_collapse_children(item, expand);
item.IsExpanded = false;
}
}
}
}
As a note: isExpanded is half a step above useless. I can set it to false when it is true and it will not collapse anything more than the highest level selected.
Thanks!
I achieve expanding all nodes in a TreeView as follows (I have a similar function to collapse all nodes):
foreach (var treeViewItem in MyTreeView.FindVisualDescendants<TreeViewItem>(e => !e.IsExpanded, true)) {
treeViewItem.IsExpanded = true;
}
Where FindVisualDescendants is a handy extension method:
public static IEnumerable<T> FindVisualDescendants<T>(this Visual parent, Func<T, bool> predicate, bool deepSearch) where T : Visual {
var visualChildren = new List<Visual>();
var visualChildrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var childIndex = 0; childIndex < visualChildrenCount; childIndex++) {
visualChildren.Add((Visual) VisualTreeHelper.GetChild(parent, childIndex));
}
foreach (var child in visualChildren) {
var typedChild = child as T;
if ((typedChild != null) && (predicate == null || predicate.Invoke(typedChild))) {
yield return typedChild;
if (deepSearch) {foreach (var foundVisual in FindVisualDescendants(child, predicate, true)) {
yield return foundVisual;
}
} else {
foreach (var foundVisual in FindVisualDescendants(child, predicate, deepSearch)) {
yield return foundVisual;
}
}
}
yield break;
}
private static void CollapseRecursive(TreeViewItem item)
{
// Collapse item if expanded.
if (item.IsExpanded)
{
item.IsExpanded = false;
}
// If the item has sub items...
if (item.Items.Count > 0)
{
// ... iterate them...
foreach (TreeViewItem subItem in item.Items)
{
// ... and if they themselves have sub items...
if (subItem.Items.Count > 0)
{
// ... collapse the sub item and its sub items.
CollapseRecursive(subItem);
}
}
}
}
If you're interested, here's the implementation (from Reflector) of how ExpandSubTree is actually written. I suppose you could just go the opposite way.
public void ExpandSubtree()
{
ExpandRecursive(this);
}
private static void ExpandRecursive(TreeViewItem item)
{
if (item != null)
{
if (!item.IsExpanded)
{
item.SetCurrentValueInternal(IsExpandedProperty, BooleanBoxes.TrueBox);
}
item.ApplyTemplate();
ItemsPresenter presenter = (ItemsPresenter) item.Template.FindName("ItemsHost", item);
if (presenter != null)
{
presenter.ApplyTemplate();
}
else
{
item.UpdateLayout();
}
VirtualizingPanel itemsHost = item.ItemsHost as VirtualizingPanel;
item.ItemsHost.EnsureGenerator();
int index = 0;
int count = item.Items.Count;
while (index < count)
{
TreeViewItem item2;
if (itemsHost != null)
{
itemsHost.BringIndexIntoView(index);
item2 = (TreeViewItem) item.ItemContainerGenerator.ContainerFromIndex(index);
}
else
{
item2 = (TreeViewItem) item.ItemContainerGenerator.ContainerFromIndex(index);
item2.BringIntoView();
}
if (item2 != null)
{
ExpandRecursive(item2);
}
index++;
}
}
}
To get the ItemHost and call the EnsureGenerator you will need reflection since these are internal :
var panel = (Panel)typeof(MultiSelector).InvokeMember("ItemsHost", BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance, null, item, null);
typeof (Panel).InvokeMember("EnsureGenerator", BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance, null, panel, null);

C# implementation of deep/recursive object comparison in .net 3.5

I am looking for a C# specific , open source (or source code available) implementation of recursive, or deep, object comparison.
I currently have two graphs of live objects that I am looking to compare to each other, with the result of the comparison being a set of discrepancies in the graphs. The objects are instantiations of a set of classes that are known at run time (but not necessarily at compile time).
There is a specific requirement to be able to map from the discrepancies in the graphs, back to the objects containing the discrepancies.
I found a really nice, free implementation at www.kellermansoftware.com called Compare .NET Objects which can be found here. Highly recommended.
Appears to have relocated to github - most recent version is available here
This is a complex area; I've done some things like this at runtime, and it quickly gets messy. If possible, you might find that the simplest way to do this is to serialize the objects and compare the serialized form (perhaps xml-diff and XmlSerializer). This is complicated a little by the types not being known until runtime, but not hugely (you can always use new XmlSerializer(obj.GetType()) etc).
That would be my default approach, anyway.
Here's an NUnit 2.4.6 custom constraint we use for comparing complex graphs. It supports embedded collections, parent references, setting tolerance for numeric comparisons, identifying field names to ignore (even deep within the hierarchy), and decorating types to be always ignored.
I'm sure this code can be adapted to be used outside NUnit, the bulk of the code isn't dependent on NUnit.
We use this in thousands of unit tests.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using NUnit.Framework;
using NUnit.Framework.Constraints;
namespace Tests
{
public class ContentsEqualConstraint : Constraint
{
private readonly object expected;
private Constraint failedEquality;
private string expectedDescription;
private string actualDescription;
private readonly Stack<string> typePath = new Stack<string>();
private string typePathExpanded;
private readonly HashSet<string> _ignoredNames = new HashSet<string>();
private readonly HashSet<Type> _ignoredTypes = new HashSet<Type>();
private readonly LinkedList<Type> _ignoredInterfaces = new LinkedList<Type>();
private readonly LinkedList<string> _ignoredSuffixes = new LinkedList<string>();
private readonly IDictionary<Type, Func<object, object, bool>> _predicates = new Dictionary<Type, Func<object, object, bool>>();
private bool _withoutSort;
private int _maxRecursion = int.MaxValue;
private readonly HashSet<VisitedComparison> _visitedObjects = new HashSet<VisitedComparison>();
private static readonly HashSet<string> _globallyIgnoredNames = new HashSet<string>();
private static readonly HashSet<Type> _globallyIgnoredTypes = new HashSet<Type>();
private static readonly LinkedList<Type> _globallyIgnoredInterfaces = new LinkedList<Type>();
private static object _regionalTolerance;
public ContentsEqualConstraint(object expectedValue)
{
expected = expectedValue;
}
public ContentsEqualConstraint Comparing<T>(Func<T, T, bool> predicate)
{
Type t = typeof (T);
if (predicate == null)
{
_predicates.Remove(t);
}
else
{
_predicates[t] = (x, y) => predicate((T) x, (T) y);
}
return this;
}
public ContentsEqualConstraint Ignoring(string fieldName)
{
_ignoredNames.Add(fieldName);
return this;
}
public ContentsEqualConstraint Ignoring(Type fieldType)
{
if (fieldType.IsInterface)
{
_ignoredInterfaces.AddFirst(fieldType);
}
else
{
_ignoredTypes.Add(fieldType);
}
return this;
}
public ContentsEqualConstraint IgnoringSuffix(string suffix)
{
if (string.IsNullOrEmpty(suffix))
{
throw new ArgumentNullException("suffix");
}
_ignoredSuffixes.AddLast(suffix);
return this;
}
public ContentsEqualConstraint WithoutSort()
{
_withoutSort = true;
return this;
}
public ContentsEqualConstraint RecursingOnly(int levels)
{
_maxRecursion = levels;
return this;
}
public static void GlobalIgnore(string fieldName)
{
_globallyIgnoredNames.Add(fieldName);
}
public static void GlobalIgnore(Type fieldType)
{
if (fieldType.IsInterface)
{
_globallyIgnoredInterfaces.AddFirst(fieldType);
}
else
{
_globallyIgnoredTypes.Add(fieldType);
}
}
public static IDisposable RegionalIgnore(string fieldName)
{
return new RegionalIgnoreTracker(fieldName);
}
public static IDisposable RegionalIgnore(Type fieldType)
{
return new RegionalIgnoreTracker(fieldType);
}
public static IDisposable RegionalWithin(object tolerance)
{
return new RegionalWithinTracker(tolerance);
}
public override bool Matches(object actualValue)
{
typePathExpanded = null;
actual = actualValue;
return Matches(expected, actualValue);
}
private bool Matches(object expectedValue, object actualValue)
{
bool matches = true;
if (!MatchesNull(expectedValue, actualValue, ref matches))
{
return matches;
}
// DatesEqualConstraint supports tolerance in dates but works as equal constraint for everything else
Constraint eq = new DatesEqualConstraint(expectedValue).Within(tolerance ?? _regionalTolerance);
if (eq.Matches(actualValue))
{
return true;
}
if (MatchesVisited(expectedValue, actualValue, ref matches))
{
if (MatchesDictionary(expectedValue, actualValue, ref matches) &&
MatchesList(expectedValue, actualValue, ref matches) &&
MatchesType(expectedValue, actualValue, ref matches) &&
MatchesPredicate(expectedValue, actualValue, ref matches))
{
MatchesFields(expectedValue, actualValue, eq, ref matches);
}
}
return matches;
}
private bool MatchesNull(object expectedValue, object actualValue, ref bool matches)
{
if (IsNullEquivalent(expectedValue))
{
expectedValue = null;
}
if (IsNullEquivalent(actualValue))
{
actualValue = null;
}
if (expectedValue == null && actualValue == null)
{
matches = true;
return false;
}
if (expectedValue == null)
{
expectedDescription = "null";
actualDescription = "NOT null";
matches = Failure;
return false;
}
if (actualValue == null)
{
expectedDescription = "not null";
actualDescription = "null";
matches = Failure;
return false;
}
return true;
}
private bool MatchesType(object expectedValue, object actualValue, ref bool matches)
{
Type expectedType = expectedValue.GetType();
Type actualType = actualValue.GetType();
if (expectedType != actualType)
{
try
{
Convert.ChangeType(actualValue, expectedType);
}
catch(InvalidCastException)
{
expectedDescription = expectedType.FullName;
actualDescription = actualType.FullName;
matches = Failure;
return false;
}
}
return true;
}
private bool MatchesPredicate(object expectedValue, object actualValue, ref bool matches)
{
Type t = expectedValue.GetType();
Func<object, object, bool> predicate;
if (_predicates.TryGetValue(t, out predicate))
{
matches = predicate(expectedValue, actualValue);
return false;
}
return true;
}
private bool MatchesVisited(object expectedValue, object actualValue, ref bool matches)
{
var c = new VisitedComparison(expectedValue, actualValue);
if (_visitedObjects.Contains(c))
{
matches = true;
return false;
}
_visitedObjects.Add(c);
return true;
}
private bool MatchesDictionary(object expectedValue, object actualValue, ref bool matches)
{
if (expectedValue is IDictionary && actualValue is IDictionary)
{
var expectedDictionary = (IDictionary)expectedValue;
var actualDictionary = (IDictionary)actualValue;
if (expectedDictionary.Count != actualDictionary.Count)
{
expectedDescription = expectedDictionary.Count + " item dictionary";
actualDescription = actualDictionary.Count + " item dictionary";
matches = Failure;
return false;
}
foreach (DictionaryEntry expectedEntry in expectedDictionary)
{
if (!actualDictionary.Contains(expectedEntry.Key))
{
expectedDescription = expectedEntry.Key + " exists";
actualDescription = expectedEntry.Key + " does not exist";
matches = Failure;
return false;
}
if (CanRecurseFurther)
{
typePath.Push(expectedEntry.Key.ToString());
if (!Matches(expectedEntry.Value, actualDictionary[expectedEntry.Key]))
{
matches = Failure;
return false;
}
typePath.Pop();
}
}
matches = true;
return false;
}
return true;
}
private bool MatchesList(object expectedValue, object actualValue, ref bool matches)
{
if (!(expectedValue is IList && actualValue is IList))
{
return true;
}
var expectedList = (IList) expectedValue;
var actualList = (IList) actualValue;
if (!Matches(expectedList.Count, actualList.Count))
{
matches = false;
}
else
{
if (CanRecurseFurther)
{
int max = expectedList.Count;
if (max != 0 && !_withoutSort)
{
SafeSort(expectedList);
SafeSort(actualList);
}
for (int i = 0; i < max; i++)
{
typePath.Push(i.ToString());
if (!Matches(expectedList[i], actualList[i]))
{
matches = false;
return false;
}
typePath.Pop();
}
}
matches = true;
}
return false;
}
private void MatchesFields(object expectedValue, object actualValue, Constraint equalConstraint, ref bool matches)
{
Type expectedType = expectedValue.GetType();
FieldInfo[] fields = expectedType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
// should have passed the EqualConstraint check
if (expectedType.IsPrimitive ||
expectedType == typeof(string) ||
expectedType == typeof(Guid) ||
fields.Length == 0)
{
failedEquality = equalConstraint;
matches = Failure;
return;
}
if (expectedType == typeof(DateTime))
{
var expectedDate = (DateTime)expectedValue;
var actualDate = (DateTime)actualValue;
if (Math.Abs((expectedDate - actualDate).TotalSeconds) > 3.0)
{
failedEquality = equalConstraint;
matches = Failure;
return;
}
matches = true;
return;
}
if (CanRecurseFurther)
{
while(true)
{
foreach (FieldInfo field in fields)
{
if (!Ignore(field))
{
typePath.Push(field.Name);
if (!Matches(GetValue(field, expectedValue), GetValue(field, actualValue)))
{
matches = Failure;
return;
}
typePath.Pop();
}
}
expectedType = expectedType.BaseType;
if (expectedType == null)
{
break;
}
fields = expectedType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
}
}
matches = true;
return;
}
private bool Ignore(FieldInfo field)
{
if (_ignoredNames.Contains(field.Name) ||
_ignoredTypes.Contains(field.FieldType) ||
_globallyIgnoredNames.Contains(field.Name) ||
_globallyIgnoredTypes.Contains(field.FieldType) ||
field.GetCustomAttributes(typeof (IgnoreContentsAttribute), false).Length != 0)
{
return true;
}
foreach(string ignoreSuffix in _ignoredSuffixes)
{
if (field.Name.EndsWith(ignoreSuffix))
{
return true;
}
}
foreach (Type ignoredInterface in _ignoredInterfaces)
{
if (ignoredInterface.IsAssignableFrom(field.FieldType))
{
return true;
}
}
return false;
}
private static bool Failure
{
get
{
return false;
}
}
private static bool IsNullEquivalent(object value)
{
return value == null ||
value == DBNull.Value ||
(value is int && (int) value == int.MinValue) ||
(value is double && (double) value == double.MinValue) ||
(value is DateTime && (DateTime) value == DateTime.MinValue) ||
(value is Guid && (Guid) value == Guid.Empty) ||
(value is IList && ((IList)value).Count == 0);
}
private static object GetValue(FieldInfo field, object source)
{
try
{
return field.GetValue(source);
}
catch(Exception ex)
{
return ex;
}
}
public override void WriteMessageTo(MessageWriter writer)
{
if (TypePath.Length != 0)
{
writer.WriteLine("Failure on " + TypePath);
}
if (failedEquality != null)
{
failedEquality.WriteMessageTo(writer);
}
else
{
base.WriteMessageTo(writer);
}
}
public override void WriteDescriptionTo(MessageWriter writer)
{
writer.Write(expectedDescription);
}
public override void WriteActualValueTo(MessageWriter writer)
{
writer.Write(actualDescription);
}
private string TypePath
{
get
{
if (typePathExpanded == null)
{
string[] p = typePath.ToArray();
Array.Reverse(p);
var text = new StringBuilder(128);
bool isFirst = true;
foreach(string part in p)
{
if (isFirst)
{
text.Append(part);
isFirst = false;
}
else
{
int i;
if (int.TryParse(part, out i))
{
text.Append("[" + part + "]");
}
else
{
text.Append("." + part);
}
}
}
typePathExpanded = text.ToString();
}
return typePathExpanded;
}
}
private bool CanRecurseFurther
{
get
{
return typePath.Count < _maxRecursion;
}
}
private static bool SafeSort(IList list)
{
if (list == null)
{
return false;
}
if (list.Count < 2)
{
return true;
}
try
{
object first = FirstNonNull(list) as IComparable;
if (first == null)
{
return false;
}
if (list is Array)
{
Array.Sort((Array)list);
return true;
}
return CallIfExists(list, "Sort");
}
catch
{
return false;
}
}
private static object FirstNonNull(IEnumerable enumerable)
{
if (enumerable == null)
{
throw new ArgumentNullException("enumerable");
}
foreach (object item in enumerable)
{
if (item != null)
{
return item;
}
}
return null;
}
private static bool CallIfExists(object instance, string method)
{
if (instance == null)
{
throw new ArgumentNullException("instance");
}
if (String.IsNullOrEmpty(method))
{
throw new ArgumentNullException("method");
}
Type target = instance.GetType();
MethodInfo m = target.GetMethod(method, new Type[0]);
if (m != null)
{
m.Invoke(instance, null);
return true;
}
return false;
}
#region VisitedComparison Helper
private class VisitedComparison
{
private readonly object _expected;
private readonly object _actual;
public VisitedComparison(object expected, object actual)
{
_expected = expected;
_actual = actual;
}
public override int GetHashCode()
{
return GetHashCode(_expected) ^ GetHashCode(_actual);
}
private static int GetHashCode(object o)
{
if (o == null)
{
return 0;
}
return o.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != typeof(VisitedComparison))
{
return false;
}
var other = (VisitedComparison) obj;
return _expected == other._expected &&
_actual == other._actual;
}
}
#endregion
#region RegionalIgnoreTracker Helper
private class RegionalIgnoreTracker : IDisposable
{
private readonly string _fieldName;
private readonly Type _fieldType;
public RegionalIgnoreTracker(string fieldName)
{
if (!_globallyIgnoredNames.Add(fieldName))
{
_globallyIgnoredNames.Add(fieldName);
_fieldName = fieldName;
}
}
public RegionalIgnoreTracker(Type fieldType)
{
if (!_globallyIgnoredTypes.Add(fieldType))
{
_globallyIgnoredTypes.Add(fieldType);
_fieldType = fieldType;
}
}
public void Dispose()
{
if (_fieldName != null)
{
_globallyIgnoredNames.Remove(_fieldName);
}
if (_fieldType != null)
{
_globallyIgnoredTypes.Remove(_fieldType);
}
}
}
#endregion
#region RegionalWithinTracker Helper
private class RegionalWithinTracker : IDisposable
{
public RegionalWithinTracker(object tolerance)
{
_regionalTolerance = tolerance;
}
public void Dispose()
{
_regionalTolerance = null;
}
}
#endregion
#region IgnoreContentsAttribute
[AttributeUsage(AttributeTargets.Field)]
public sealed class IgnoreContentsAttribute : Attribute
{
}
#endregion
}
public class DatesEqualConstraint : EqualConstraint
{
private readonly object _expected;
public DatesEqualConstraint(object expectedValue) : base(expectedValue)
{
_expected = expectedValue;
}
public override bool Matches(object actualValue)
{
if (tolerance != null && tolerance is TimeSpan)
{
if (_expected is DateTime && actualValue is DateTime)
{
var expectedDate = (DateTime) _expected;
var actualDate = (DateTime) actualValue;
var toleranceSpan = (TimeSpan) tolerance;
if ((actualDate - expectedDate).Duration() <= toleranceSpan)
{
return true;
}
}
tolerance = null;
}
return base.Matches(actualValue);
}
}
}
This is actually a simple process. Using reflection you can compare each field on the object.
public static Boolean ObjectMatches(Object x, Object y)
{
if (x == null && y == null)
return true;
else if ((x == null && y != null) || (x != null && y == null))
return false;
Type tx = x.GetType();
Type ty = y.GetType();
if (tx != ty)
return false;
foreach(FieldInfo field in tx.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
if (field.FieldType.IsValueType && (field.GetValue(x).ToString() != field.GetValue(y).ToString()))
return false;
else if (field.FieldType.IsClass && !ObjectMatches(field.GetValue(x), field.GetValue(y)))
return false;
}
return true;
}
Using the Nuget suggested by Jesse and this code I managed to compare two objects with great results.
using KellermanSoftware.CompareNetObjects;
using System;
namespace MyProgram.UnitTestHelper
{
public class ObjectComparer
{
public static bool ObjectsHaveSameValues(object first, object second)
{
CompareLogic cl = new CompareLogic();
ComparisonResult result = cl.Compare(first, second);
if (!result.AreEqual)
Console.WriteLine(result.DifferencesString);
return result.AreEqual;
}
}
}
Here is a simple comparer that we've used with unit testing to assert that two objects have equal properties. It's a mash-up of ideas found in various articles, and handles circular references.
public static class TestHelper
{
public static void AssertAreEqual(Object expected, Object actual, String name)
{
// Start a new check with an empty list of actual objects checked
// The list of actual objects checked is used to ensure that circular references don't result in infinite recursion
List<Object> actualObjectsChecked = new List<Object>();
AssertAreEqual(expected, actual, name, actualObjectsChecked);
}
private static void AssertAreEqual(Object expected, Object actual, String name, List<Object> actualObjectsChecked)
{
// Just return if already checked the actual object
if (actualObjectsChecked.Contains(actual))
{
return;
}
actualObjectsChecked.Add(actual);
// If both expected and actual are null, they are considered equal
if (expected == null && actual == null)
{
return;
}
if (expected == null && actual != null)
{
Assert.Fail(String.Format("The actual value of {0} was not null when null was expected.", name));
}
if (expected != null && actual == null)
{
Assert.Fail(String.Format("The actual value of {0} was null when an instance was expected.", name));
}
// Get / check type info
// Note: GetType always returns instantiated (i.e. most derived) type
Type expectedType = expected.GetType();
Type actualType = actual.GetType();
if (expectedType != actualType)
{
Assert.Fail(String.Format("The actual type of {0} was not the same as the expected type.", name));
}
// If expected is a Primitive, Value, or IEquatable type, assume Equals is sufficient to check
// Note: Every IEquatable type should have also overridden Object.Equals
if (expectedType.IsPrimitive || expectedType.IsValueType || expectedType.IsIEquatable())
{
Assert.IsTrue(expected.Equals(actual), "The actual {0} is not equal to the expected.", name);
return;
}
// If expected is an IEnumerable type, assume comparing enumerated items is sufficient to check
IEnumerable<Object> expectedEnumerable = expected as IEnumerable<Object>;
IEnumerable<Object> actualEnumerable = actual as IEnumerable<Object>;
if ((expectedEnumerable != null) && (actualEnumerable != null))
{
Int32 actualEnumerableCount = actualEnumerable.Count();
Int32 expectedEnumerableCount = expectedEnumerable.Count();
// Check size first
if (actualEnumerableCount != expectedEnumerableCount)
{
Assert.Fail(String.Format("The actual number of enumerable items in {0} did not match the expected number.", name));
}
// Check items in order, assuming order is the same
for (int i = 0; i < actualEnumerableCount; ++i)
{
AssertAreEqual(expectedEnumerable.ElementAt(i), actualEnumerable.ElementAt(i), String.Format("{0}[{1}]", name, i), actualObjectsChecked);
}
return;
}
// If expected is not a Primitive, Value, IEquatable, or Ienumerable type, assume comparing properties is sufficient to check
// Iterate through properties
foreach (PropertyInfo propertyInfo in actualType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
// Skip properties that can't be read or require parameters
if ((!propertyInfo.CanRead) || (propertyInfo.GetIndexParameters().Length != 0))
{
continue;
}
// Get properties from both
Object actualProperty = propertyInfo.GetValue(actual, null);
Object expectedProperty = propertyInfo.GetValue(expected, null);
AssertAreEqual(expectedProperty, actualProperty, String.Format("{0}.{1}", name, propertyInfo.Name), actualObjectsChecked);
}
}
public static Boolean IsIEquatable(this Type type)
{
return type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEquatable<>));
}
}

Categories