From fe5623996076b909fe16d875f67f5588b5f05888 Mon Sep 17 00:00:00 2001 From: Jonas Nyrup Date: Sat, 17 Nov 2018 09:05:45 +0100 Subject: [PATCH] Optimize performance of IncludeMemberByPathSelectionRule (#969) --- Src/FluentAssertions/Common/MemberPath.cs | 41 +++++++++++-------- .../ExcludeMemberByPathSelectionRule.cs | 2 +- .../IncludeMemberByPathSelectionRule.cs | 4 +- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Src/FluentAssertions/Common/MemberPath.cs b/Src/FluentAssertions/Common/MemberPath.cs index 68753533bd..bee06b0640 100644 --- a/Src/FluentAssertions/Common/MemberPath.cs +++ b/Src/FluentAssertions/Common/MemberPath.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; namespace FluentAssertions.Common @@ -12,47 +11,53 @@ internal class MemberPath { private readonly Type declaringType; private readonly string dottedPath; - private readonly List segments = new List(); + + private string[] segments; public MemberPath(Type declaringType, string dottedPath) { this.declaringType = declaringType; this.dottedPath = dottedPath; - segments.AddRange(Segmentize(dottedPath)); } - public bool IsParentOrChildOf(string candidate) + public bool IsParentOrChildOf(MemberPath candidate) { return IsParent(candidate) || IsChild(candidate); } - public bool IsSameAs(string candidate, Type memberDeclaringType) + public bool IsSameAs(MemberPath candidate) { - string[] candidateSegments = Segmentize(candidate); + if (candidate.declaringType != declaringType) + { + return false; + } + + string[] segments = GetSegments(); + string[] candidateSegments = candidate.GetSegments(); - return memberDeclaringType == declaringType && candidateSegments.SequenceEqual(segments); + return candidateSegments.SequenceEqual(segments); } - private bool IsChild(string candidate) + private bool IsChild(MemberPath candidate) { - string[] candidateSegments = Segmentize(candidate); + string[] segments = GetSegments(); + string[] candidateSegments = candidate.GetSegments(); - return candidateSegments.Length > segments.Count && - candidateSegments.Take(segments.Count).SequenceEqual(segments); + return candidateSegments.Length > segments.Length && + candidateSegments.Take(segments.Length).SequenceEqual(segments); } - private bool IsParent(string candidate) + private bool IsParent(MemberPath candidate) { - string[] candidateSegments = Segmentize(candidate); + string[] segments = GetSegments(); + string[] candidateSegments = candidate.GetSegments(); - return candidateSegments.Length < segments.Count + return candidateSegments.Length < segments.Length && candidateSegments.SequenceEqual(segments.Take(candidateSegments.Length)); } - private static string[] Segmentize(string dottedPath) - { - return dottedPath.Split(new[] { '.', '[', ']' }, StringSplitOptions.RemoveEmptyEntries); - } + private string[] GetSegments() => + segments ?? (segments = dottedPath.Split(new[] { '.', '[', ']' }, StringSplitOptions.RemoveEmptyEntries)); public override string ToString() { diff --git a/Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs b/Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs index 12d0645d6d..aff3ff6df2 100644 --- a/Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs +++ b/Src/FluentAssertions/Equivalency/Selection/ExcludeMemberByPathSelectionRule.cs @@ -22,7 +22,7 @@ public ExcludeMemberByPathSelectionRule(MemberPath pathToExclude) string currentPath, IMemberInfo context) { return selectedMembers - .Where(memberInfo => !memberToExclude.IsSameAs(currentPath.Combine(memberInfo.Name), memberInfo.DeclaringType)) + .Where(memberInfo => !memberToExclude.IsSameAs(new MemberPath(memberInfo.DeclaringType, currentPath.Combine(memberInfo.Name)))) .ToArray(); } diff --git a/Src/FluentAssertions/Equivalency/Selection/IncludeMemberByPathSelectionRule.cs b/Src/FluentAssertions/Equivalency/Selection/IncludeMemberByPathSelectionRule.cs index f428475108..87f82c7972 100644 --- a/Src/FluentAssertions/Equivalency/Selection/IncludeMemberByPathSelectionRule.cs +++ b/Src/FluentAssertions/Equivalency/Selection/IncludeMemberByPathSelectionRule.cs @@ -25,8 +25,8 @@ public IncludeMemberByPathSelectionRule(MemberPath pathToInclude) { var matchingMembers = from member in context.RuntimeType.GetNonPrivateMembers() - let memberPath = currentPath.Combine(member.Name) - where memberToInclude.IsSameAs(memberPath, member.DeclaringType) || + let memberPath = new MemberPath(member.DeclaringType, currentPath.Combine(member.Name)) + where memberToInclude.IsSameAs(memberPath) || memberToInclude.IsParentOrChildOf(memberPath) select member;