Lucene.Net.Search.FieldCacheDocIdSet: Added public constructor with predicate parameter for filtering without having to hand code a class
diff --git a/src/Lucene.Net/Search/DocTermOrdsRangeFilter.cs b/src/Lucene.Net/Search/DocTermOrdsRangeFilter.cs
index 7f1343e..e2d0d20 100644
--- a/src/Lucene.Net/Search/DocTermOrdsRangeFilter.cs
+++ b/src/Lucene.Net/Search/DocTermOrdsRangeFilter.cs
@@ -125,27 +125,7 @@
 
                 if (Debugging.AssertsEnabled) Debugging.Assert(inclusiveLowerPoint >= 0 && inclusiveUpperPoint >= 0);
 
-                return new FieldCacheDocIdSetAnonymousInnerClassHelper(this, context.AtomicReader.MaxDoc, acceptDocs, docTermOrds, inclusiveLowerPoint, inclusiveUpperPoint);
-            }
-
-            private class FieldCacheDocIdSetAnonymousInnerClassHelper : FieldCacheDocIdSet
-            {
-                private readonly DocTermOrdsRangeFilterAnonymousInnerClassHelper outerInstance;
-
-                private readonly SortedSetDocValues docTermOrds;
-                private readonly long inclusiveLowerPoint;
-                private readonly long inclusiveUpperPoint;
-
-                public FieldCacheDocIdSetAnonymousInnerClassHelper(DocTermOrdsRangeFilterAnonymousInnerClassHelper outerInstance, int maxDoc, IBits acceptDocs, SortedSetDocValues docTermOrds, long inclusiveLowerPoint, long inclusiveUpperPoint)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.outerInstance = outerInstance;
-                    this.docTermOrds = docTermOrds;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override sealed bool MatchDoc(int doc)
+                return new FieldCacheDocIdSet(context.AtomicReader.MaxDoc, acceptDocs, (doc) =>
                 {
                     docTermOrds.SetDocument(doc);
                     long ord;
@@ -161,7 +141,7 @@
                         }
                     }
                     return false;
-                }
+                });
             }
         }
 
diff --git a/src/Lucene.Net/Search/DocTermOrdsRewriteMethod.cs b/src/Lucene.Net/Search/DocTermOrdsRewriteMethod.cs
index ef53a4d..16bf23b 100644
--- a/src/Lucene.Net/Search/DocTermOrdsRewriteMethod.cs
+++ b/src/Lucene.Net/Search/DocTermOrdsRewriteMethod.cs
@@ -113,8 +113,20 @@
                 {
                     return null;
                 }
-
-                return new FieldCacheDocIdSetAnonymousInnerClassHelper(this, context.Reader.MaxDoc, acceptDocs, docTermOrds, termSet);
+                return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    docTermOrds.SetDocument(doc);
+                    long ord;
+                    // TODO: we could track max bit set and early terminate (since they come in sorted order)
+                    while ((ord = docTermOrds.NextOrd()) != SortedSetDocValues.NO_MORE_ORDS)
+                    {
+                        if (termSet.Get(ord))
+                        {
+                            return true;
+                        }
+                    }
+                    return false;
+                });
             }
 
             private class TermsAnonymousInnerClassHelper : Terms
@@ -152,37 +164,6 @@
 
                 public override bool HasPayloads => false;
             }
-
-            private class FieldCacheDocIdSetAnonymousInnerClassHelper : FieldCacheDocIdSet
-            {
-                private readonly MultiTermQueryDocTermOrdsWrapperFilter outerInstance;
-
-                private SortedSetDocValues docTermOrds;
-                private Int64BitSet termSet;
-
-                public FieldCacheDocIdSetAnonymousInnerClassHelper(MultiTermQueryDocTermOrdsWrapperFilter outerInstance, int maxDoc, IBits acceptDocs, SortedSetDocValues docTermOrds, Int64BitSet termSet)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.outerInstance = outerInstance;
-                    this.docTermOrds = docTermOrds;
-                    this.termSet = termSet;
-                }
-
-                protected internal override sealed bool MatchDoc(int doc)
-                {
-                    docTermOrds.SetDocument(doc);
-                    long ord;
-                    // TODO: we could track max bit set and early terminate (since they come in sorted order)
-                    while ((ord = docTermOrds.NextOrd()) != SortedSetDocValues.NO_MORE_ORDS)
-                    {
-                        if (termSet.Get(ord))
-                        {
-                            return true;
-                        }
-                    }
-                    return false;
-                }
-            }
         }
 
         public override bool Equals(object obj)
diff --git a/src/Lucene.Net/Search/FieldCacheDocIdSet.cs b/src/Lucene.Net/Search/FieldCacheDocIdSet.cs
index 15cbba7..68ad720 100644
--- a/src/Lucene.Net/Search/FieldCacheDocIdSet.cs
+++ b/src/Lucene.Net/Search/FieldCacheDocIdSet.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Lucene.Net.Search
 {
     /*
@@ -31,12 +33,24 @@
     /// <para/>
     /// @lucene.internal
     /// </summary>
-    public abstract class FieldCacheDocIdSet : DocIdSet
+    public class FieldCacheDocIdSet : DocIdSet
     {
         protected readonly int m_maxDoc;
         protected readonly IBits m_acceptDocs;
+        private readonly Predicate<int> matchDoc;
+        private readonly bool hasMatchDoc;
 
-        public FieldCacheDocIdSet(int maxDoc, IBits acceptDocs)
+        // LUCENENET specific - added constructor to allow the class to be used without hand-coding
+        // a subclass by passing a predicate.
+        public FieldCacheDocIdSet(int maxDoc, IBits acceptDocs, Predicate<int> matchDoc)
+        {
+            this.matchDoc = matchDoc ?? throw new ArgumentNullException(nameof(matchDoc));
+            this.hasMatchDoc = true;
+            this.m_maxDoc = maxDoc;
+            this.m_acceptDocs = acceptDocs;
+        }
+
+        protected FieldCacheDocIdSet(int maxDoc, IBits acceptDocs)
         {
             this.m_maxDoc = maxDoc;
             this.m_acceptDocs = acceptDocs;
@@ -45,7 +59,7 @@
         /// <summary>
         /// This method checks, if a doc is a hit
         /// </summary>
-        protected internal abstract bool MatchDoc(int doc);
+        protected internal virtual bool MatchDoc(int doc) => hasMatchDoc && matchDoc(doc);
 
         /// <summary>
         /// This DocIdSet is always cacheable (does not go back
diff --git a/src/Lucene.Net/Search/FieldCacheRangeFilter.cs b/src/Lucene.Net/Search/FieldCacheRangeFilter.cs
index 716ba77..76dcdb9 100644
--- a/src/Lucene.Net/Search/FieldCacheRangeFilter.cs
+++ b/src/Lucene.Net/Search/FieldCacheRangeFilter.cs
@@ -65,27 +65,6 @@
 #endif
         private class AnonymousStringFieldCacheRangeFilter : FieldCacheRangeFilter<string>
         {
-            private class AnonymousClassFieldCacheDocIdSet : FieldCacheDocIdSet
-            {
-                private SortedDocValues fcsi;
-                private int inclusiveLowerPoint;
-                private int inclusiveUpperPoint;
-
-                internal AnonymousClassFieldCacheDocIdSet(SortedDocValues fcsi, int inclusiveLowerPoint, int inclusiveUpperPoint, int maxDoc, IBits acceptDocs)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.fcsi = fcsi;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override bool MatchDoc(int doc)
-                {
-                    int docOrd = fcsi.GetOrd(doc);
-                    return docOrd >= inclusiveLowerPoint && docOrd <= inclusiveUpperPoint;
-                }
-            }
-
             internal AnonymousStringFieldCacheRangeFilter(string field, string lowerVal, string upperVal, bool includeLower, bool includeUpper)
                 : base(field, null, lowerVal, upperVal, includeLower, includeUpper)
             {
@@ -144,7 +123,11 @@
 
                 if (Debugging.AssertsEnabled) Debugging.Assert(inclusiveLowerPoint >= 0 && inclusiveUpperPoint >= 0);
 
-                return new AnonymousClassFieldCacheDocIdSet(fcsi, inclusiveLowerPoint, inclusiveUpperPoint, context.Reader.MaxDoc, acceptDocs);
+                return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    int docOrd = fcsi.GetOrd(doc);
+                    return docOrd >= inclusiveLowerPoint && docOrd <= inclusiveUpperPoint;
+                });
             }
         }
 
@@ -153,27 +136,6 @@
 #endif
         private class AnonymousBytesRefFieldCacheRangeFilter : FieldCacheRangeFilter<BytesRef>
         {
-            private class AnonymousClassFieldCacheDocIdSet : FieldCacheDocIdSet
-            {
-                private SortedDocValues fcsi;
-                private int inclusiveLowerPoint;
-                private int inclusiveUpperPoint;
-
-                internal AnonymousClassFieldCacheDocIdSet(SortedDocValues fcsi, int inclusiveLowerPoint, int inclusiveUpperPoint, int maxDoc, IBits acceptDocs)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.fcsi = fcsi;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override bool MatchDoc(int doc)
-                {
-                    int docOrd = fcsi.GetOrd(doc);
-                    return docOrd >= inclusiveLowerPoint && docOrd <= inclusiveUpperPoint;
-                }
-            }
-
             internal AnonymousBytesRefFieldCacheRangeFilter(string field, BytesRef lowerVal, BytesRef upperVal, bool includeLower, bool includeUpper)
                 : base(field, null, lowerVal, upperVal, includeLower, includeUpper)
             {
@@ -232,7 +194,11 @@
 
                 if (Debugging.AssertsEnabled) Debugging.Assert(inclusiveLowerPoint >= 0 && inclusiveUpperPoint >= 0);
 
-                return new AnonymousClassFieldCacheDocIdSet(fcsi, inclusiveLowerPoint, inclusiveUpperPoint, context.AtomicReader.MaxDoc, acceptDocs);
+                return new FieldCacheDocIdSet(context.AtomicReader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    int docOrd = fcsi.GetOrd(doc);
+                    return docOrd >= inclusiveLowerPoint && docOrd <= inclusiveUpperPoint;
+                });
             }
         }
 
@@ -241,27 +207,6 @@
 #endif
         private class AnonymousSbyteFieldCacheRangeFilter : FieldCacheRangeFilter<sbyte?>
         {
-            private class AnonymousClassFieldCacheDocIdSet : FieldCacheDocIdSet
-            {
-                private FieldCache.Bytes values;
-                private sbyte inclusiveLowerPoint;
-                private sbyte inclusiveUpperPoint;
-
-                internal AnonymousClassFieldCacheDocIdSet(FieldCache.Bytes values, sbyte inclusiveLowerPoint, sbyte inclusiveUpperPoint, int maxDoc, IBits acceptDocs)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.values = values;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override bool MatchDoc(int doc)
-                {
-                    sbyte value = (sbyte)values.Get(doc);
-                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
-                }
-            }
-
             internal AnonymousSbyteFieldCacheRangeFilter(string field, FieldCache.IParser parser, sbyte? lowerVal, sbyte? upperVal, bool includeLower, bool includeUpper)
                 : base(field, parser, lowerVal, upperVal, includeLower, includeUpper)
             {
@@ -301,7 +246,11 @@
 #pragma warning restore 612, 618
 
                 // we only request the usage of termDocs, if the range contains 0
-                return new AnonymousClassFieldCacheDocIdSet(values, inclusiveLowerPoint, inclusiveUpperPoint, context.AtomicReader.MaxDoc, acceptDocs);
+                return new FieldCacheDocIdSet(context.AtomicReader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    sbyte value = (sbyte)values.Get(doc);
+                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
+                });
             }
         }
 
@@ -310,27 +259,6 @@
 #endif
         private class AnonymousInt16FieldCacheRangeFilter : FieldCacheRangeFilter<short?>
         {
-            private class AnonymousClassFieldCacheDocIdSet : FieldCacheDocIdSet
-            {
-                private FieldCache.Int16s values;
-                private short inclusiveLowerPoint;
-                private short inclusiveUpperPoint;
-
-                internal AnonymousClassFieldCacheDocIdSet(FieldCache.Int16s values, short inclusiveLowerPoint, short inclusiveUpperPoint, int maxDoc, IBits acceptDocs)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.values = values;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override bool MatchDoc(int doc)
-                {
-                    short value = values.Get(doc);
-                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
-                }
-            }
-
             internal AnonymousInt16FieldCacheRangeFilter(string field, FieldCache.IParser parser, short? lowerVal, short? upperVal, bool includeLower, bool includeUpper)
                 : base(field, parser, lowerVal, upperVal, includeLower, includeUpper)
             {
@@ -371,7 +299,11 @@
 #pragma warning restore 612, 618
 
                 // we only request the usage of termDocs, if the range contains 0
-                return new AnonymousClassFieldCacheDocIdSet(values, inclusiveLowerPoint, inclusiveUpperPoint, context.Reader.MaxDoc, acceptDocs);
+                return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    short value = values.Get(doc);
+                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
+                });
             }
         }
 
@@ -380,27 +312,6 @@
 #endif
         private class AnonymousInt32FieldCacheRangeFilter : FieldCacheRangeFilter<int?>
         {
-            private class AnonymousClassFieldCacheDocIdSet : FieldCacheDocIdSet
-            {
-                private FieldCache.Int32s values;
-                private int inclusiveLowerPoint;
-                private int inclusiveUpperPoint;
-
-                internal AnonymousClassFieldCacheDocIdSet(FieldCache.Int32s values, int inclusiveLowerPoint, int inclusiveUpperPoint, int maxDoc, IBits acceptDocs)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.values = values;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override bool MatchDoc(int doc)
-                {
-                    int value = values.Get(doc);
-                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
-                }
-            }
-
             internal AnonymousInt32FieldCacheRangeFilter(string field, FieldCache.IParser parser, int? lowerVal, int? upperVal, bool includeLower, bool includeUpper)
                 : base(field, parser, lowerVal, upperVal, includeLower, includeUpper)
             {
@@ -438,7 +349,11 @@
 
                 FieldCache.Int32s values = FieldCache.DEFAULT.GetInt32s(context.AtomicReader, field, (FieldCache.IInt32Parser)parser, false);
                 // we only request the usage of termDocs, if the range contains 0
-                return new AnonymousClassFieldCacheDocIdSet(values, inclusiveLowerPoint, inclusiveUpperPoint, context.Reader.MaxDoc, acceptDocs);
+                return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    int value = values.Get(doc);
+                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
+                });
             }
         }
 
@@ -447,27 +362,6 @@
 #endif
         private class AnonymousInt64FieldCacheRangeFilter : FieldCacheRangeFilter<long?>
         {
-            private class AnonymousClassFieldCacheDocIdSet : FieldCacheDocIdSet
-            {
-                private FieldCache.Int64s values;
-                private long inclusiveLowerPoint;
-                private long inclusiveUpperPoint;
-
-                internal AnonymousClassFieldCacheDocIdSet(FieldCache.Int64s values, long inclusiveLowerPoint, long inclusiveUpperPoint, int maxDoc, IBits acceptDocs)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.values = values;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override bool MatchDoc(int doc)
-                {
-                    long value = values.Get(doc);
-                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
-                }
-            }
-
             internal AnonymousInt64FieldCacheRangeFilter(string field, FieldCache.IParser parser, long? lowerVal, long? upperVal, bool includeLower, bool includeUpper)
                 : base(field, parser, lowerVal, upperVal, includeLower, includeUpper)
             {
@@ -505,7 +399,11 @@
 
                 FieldCache.Int64s values = FieldCache.DEFAULT.GetInt64s(context.AtomicReader, field, (FieldCache.IInt64Parser)parser, false);
                 // we only request the usage of termDocs, if the range contains 0
-                return new AnonymousClassFieldCacheDocIdSet(values, inclusiveLowerPoint, inclusiveUpperPoint, context.Reader.MaxDoc, acceptDocs);
+                return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    long value = values.Get(doc);
+                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
+                });
             }
         }
 
@@ -514,27 +412,6 @@
 #endif
         private class AnonymousSingleFieldCacheRangeFilter : FieldCacheRangeFilter<float?>
         {
-            private class AnonymousClassFieldCacheDocIdSet : FieldCacheDocIdSet
-            {
-                private FieldCache.Singles values;
-                private float inclusiveLowerPoint;
-                private float inclusiveUpperPoint;
-
-                internal AnonymousClassFieldCacheDocIdSet(FieldCache.Singles values, float inclusiveLowerPoint, float inclusiveUpperPoint, int maxDoc, IBits acceptDocs)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.values = values;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override bool MatchDoc(int doc)
-                {
-                    float value = values.Get(doc);
-                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
-                }
-            }
-
             internal AnonymousSingleFieldCacheRangeFilter(string field, FieldCache.IParser parser, float? lowerVal, float? upperVal, bool includeLower, bool includeUpper)
                 : base(field, parser, lowerVal, upperVal, includeLower, includeUpper)
             {
@@ -577,7 +454,11 @@
                 FieldCache.Singles values = FieldCache.DEFAULT.GetSingles(context.AtomicReader, field, (FieldCache.ISingleParser)parser, false);
 
                 // we only request the usage of termDocs, if the range contains 0
-                return new AnonymousClassFieldCacheDocIdSet(values, inclusiveLowerPoint, inclusiveUpperPoint, context.Reader.MaxDoc, acceptDocs);
+                return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    float value = values.Get(doc);
+                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
+                });
             }
         }
 
@@ -586,27 +467,6 @@
 #endif
         private class AnonymousDoubleFieldCacheRangeFilter : FieldCacheRangeFilter<double?>
         {
-            private class AnonymousClassFieldCacheDocIdSet : FieldCacheDocIdSet
-            {
-                private FieldCache.Doubles values;
-                private double inclusiveLowerPoint;
-                private double inclusiveUpperPoint;
-
-                internal AnonymousClassFieldCacheDocIdSet(FieldCache.Doubles values, double inclusiveLowerPoint, double inclusiveUpperPoint, int maxDoc, IBits acceptDocs)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.values = values;
-                    this.inclusiveLowerPoint = inclusiveLowerPoint;
-                    this.inclusiveUpperPoint = inclusiveUpperPoint;
-                }
-
-                protected internal override bool MatchDoc(int doc)
-                {
-                    double value = values.Get(doc);
-                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
-                }
-            }
-
             internal AnonymousDoubleFieldCacheRangeFilter(string field, FieldCache.IParser parser, double? lowerVal, double? upperVal, bool includeLower, bool includeUpper)
                 : base(field, parser, lowerVal, upperVal, includeLower, includeUpper)
             {
@@ -649,7 +509,11 @@
                 FieldCache.Doubles values = FieldCache.DEFAULT.GetDoubles(context.AtomicReader, field, (FieldCache.IDoubleParser)parser, false);
 
                 // we only request the usage of termDocs, if the range contains 0
-                return new AnonymousClassFieldCacheDocIdSet(values, inclusiveLowerPoint, inclusiveUpperPoint, context.Reader.MaxDoc, acceptDocs);
+                return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    double value = values.Get(doc);
+                    return value >= inclusiveLowerPoint && value <= inclusiveUpperPoint;
+                });
             }
         }
 
diff --git a/src/Lucene.Net/Search/FieldCacheRewriteMethod.cs b/src/Lucene.Net/Search/FieldCacheRewriteMethod.cs
index b81be95..706beaa 100644
--- a/src/Lucene.Net/Search/FieldCacheRewriteMethod.cs
+++ b/src/Lucene.Net/Search/FieldCacheRewriteMethod.cs
@@ -98,7 +98,7 @@
                 SortedDocValues fcsi = FieldCache.DEFAULT.GetTermsIndex((context.AtomicReader), m_query.m_field);
                 // Cannot use FixedBitSet because we require long index (ord):
                 Int64BitSet termSet = new Int64BitSet(fcsi.ValueCount);
-                TermsEnum termsEnum = m_query.GetTermsEnum(new TermsAnonymousInnerClassHelper(this, fcsi));
+                TermsEnum termsEnum = m_query.GetTermsEnum(new TermsAnonymousInnerClassHelper(fcsi));
 
                 if (Debugging.AssertsEnabled) Debugging.Assert(termsEnum != null);
                 if (termsEnum.Next() != null)
@@ -118,18 +118,23 @@
                     return null;
                 }
 
-                return new FieldCacheDocIdSetAnonymousInnerClassHelper(this, context.Reader.MaxDoc, acceptDocs, fcsi, termSet);
+                return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
+                {
+                    int ord = fcsi.GetOrd(doc);
+                    if (ord == -1)
+                    {
+                        return false;
+                    }
+                    return termSet.Get(ord);
+                });
             }
 
             private class TermsAnonymousInnerClassHelper : Terms
             {
-                private readonly MultiTermQueryFieldCacheWrapperFilter outerInstance;
+                private readonly SortedDocValues fcsi;
 
-                private SortedDocValues fcsi;
-
-                public TermsAnonymousInnerClassHelper(MultiTermQueryFieldCacheWrapperFilter outerInstance, SortedDocValues fcsi)
+                public TermsAnonymousInnerClassHelper(SortedDocValues fcsi)
                 {
-                    this.outerInstance = outerInstance;
                     this.fcsi = fcsi;
                 }
 
@@ -156,32 +161,6 @@
 
                 public override bool HasPayloads => false;
             }
-
-            private class FieldCacheDocIdSetAnonymousInnerClassHelper : FieldCacheDocIdSet
-            {
-                private readonly MultiTermQueryFieldCacheWrapperFilter outerInstance;
-
-                private SortedDocValues fcsi;
-                private Int64BitSet termSet;
-
-                public FieldCacheDocIdSetAnonymousInnerClassHelper(MultiTermQueryFieldCacheWrapperFilter outerInstance, int maxDoc, IBits acceptDocs, SortedDocValues fcsi, Int64BitSet termSet)
-                    : base(maxDoc, acceptDocs)
-                {
-                    this.outerInstance = outerInstance;
-                    this.fcsi = fcsi;
-                    this.termSet = termSet;
-                }
-
-                protected internal override sealed bool MatchDoc(int doc)
-                {
-                    int ord = fcsi.GetOrd(doc);
-                    if (ord == -1)
-                    {
-                        return false;
-                    }
-                    return termSet.Get(ord);
-                }
-            }
         }
 
         public override bool Equals(object obj)
diff --git a/src/Lucene.Net/Search/FieldCacheTermsFilter.cs b/src/Lucene.Net/Search/FieldCacheTermsFilter.cs
index 0c4b2fa..2af5265 100644
--- a/src/Lucene.Net/Search/FieldCacheTermsFilter.cs
+++ b/src/Lucene.Net/Search/FieldCacheTermsFilter.cs
@@ -92,11 +92,10 @@
     ///
     /// Which filter is best is very application dependent.
     /// </summary>
-
     public class FieldCacheTermsFilter : Filter
     {
-        private string field;
-        private BytesRef[] terms;
+        private readonly string field;
+        private readonly BytesRef[] terms;
 
         public FieldCacheTermsFilter(string field, params BytesRef[] terms)
         {
@@ -128,25 +127,7 @@
                     bits.Set(ord);
                 }
             }
-            return new FieldCacheDocIdSetAnonymousInnerClassHelper(this, context.Reader.MaxDoc, acceptDocs, fcsi, bits);
-        }
-
-        private class FieldCacheDocIdSetAnonymousInnerClassHelper : FieldCacheDocIdSet
-        {
-            private readonly FieldCacheTermsFilter outerInstance;
-
-            private SortedDocValues fcsi;
-            private FixedBitSet bits;
-
-            public FieldCacheDocIdSetAnonymousInnerClassHelper(FieldCacheTermsFilter outerInstance, int maxDoc, IBits acceptDocs, SortedDocValues fcsi, FixedBitSet bits)
-                : base(maxDoc, acceptDocs)
-            {
-                this.outerInstance = outerInstance;
-                this.fcsi = fcsi;
-                this.bits = bits;
-            }
-
-            protected internal override sealed bool MatchDoc(int doc)
+            return new FieldCacheDocIdSet(context.Reader.MaxDoc, acceptDocs, (doc) =>
             {
                 int ord = fcsi.GetOrd(doc);
                 if (ord == -1)
@@ -158,7 +139,7 @@
                 {
                     return bits.Get(ord);
                 }
-            }
+            });
         }
     }
 }
\ No newline at end of file
diff --git a/src/Lucene.Net/Search/FieldValueFilter.cs b/src/Lucene.Net/Search/FieldValueFilter.cs
index 91ae83c..c11d1ab 100644
--- a/src/Lucene.Net/Search/FieldValueFilter.cs
+++ b/src/Lucene.Net/Search/FieldValueFilter.cs
@@ -79,7 +79,7 @@
                 {
                     return null;
                 }
-                return new FieldCacheDocIdSetAnonymousInnerClassHelper(this, context.AtomicReader.MaxDoc, acceptDocs, docsWithField);
+                return new FieldCacheDocIdSet(context.AtomicReader.MaxDoc, acceptDocs, (doc) => !docsWithField.Get(doc));
             }
             else
             {
@@ -93,45 +93,7 @@
                     // :-)
                     return BitsFilteredDocIdSet.Wrap((DocIdSet)docsWithField, acceptDocs);
                 }
-                return new FieldCacheDocIdSetAnonymousInnerClassHelper2(this, context.AtomicReader.MaxDoc, acceptDocs, docsWithField);
-            }
-        }
-
-        private class FieldCacheDocIdSetAnonymousInnerClassHelper : FieldCacheDocIdSet
-        {
-            private readonly FieldValueFilter outerInstance;
-
-            private IBits docsWithField;
-
-            public FieldCacheDocIdSetAnonymousInnerClassHelper(FieldValueFilter outerInstance, int maxDoc, IBits acceptDocs, IBits docsWithField)
-                : base(maxDoc, acceptDocs)
-            {
-                this.outerInstance = outerInstance;
-                this.docsWithField = docsWithField;
-            }
-
-            protected internal override sealed bool MatchDoc(int doc)
-            {
-                return !docsWithField.Get(doc);
-            }
-        }
-
-        private class FieldCacheDocIdSetAnonymousInnerClassHelper2 : FieldCacheDocIdSet
-        {
-            private readonly FieldValueFilter outerInstance;
-
-            private readonly IBits docsWithField;
-
-            public FieldCacheDocIdSetAnonymousInnerClassHelper2(FieldValueFilter outerInstance, int maxDoc, IBits acceptDocs, IBits docsWithField)
-                : base(maxDoc, acceptDocs)
-            {
-                this.outerInstance = outerInstance;
-                this.docsWithField = docsWithField;
-            }
-
-            protected internal override sealed bool MatchDoc(int doc)
-            {
-                return docsWithField.Get(doc);
+                return new FieldCacheDocIdSet(context.AtomicReader.MaxDoc, acceptDocs, (doc) => docsWithField.Get(doc));
             }
         }