NightOwl888 commented on issue #1059:
URL: https://github.com/apache/lucenenet/issues/1059#issuecomment-2543953460
> > It looks like you neatly solved access to the GroupCount field using
IGroupCountCollector, but the tests don't access the Groups property so there
is no need to find a solution to cast it AFAICT.
>
> I actually already removed that in lieu of having this do the same
approach as the other collectors in the tests, so that we're not adding an
unnecessary interface just for testing purposes. It's unlikely that someone
would need just the group counts in the real world without also accessing the
groups, so I figured it would be best to not pollute the public API with that
interface when it was only used (read: not needed, just used as one approach)
for test purposes.
-----------------
> I just had an idea, based on that last comment, if you're open to it. We
could create non-generic interfaces (not abstract classes) that do not expose
collections, but give you APIs to retrieve items. By using an interface instead
of an abstract class, we wouldn't be forced to convert to/from object except
for in the explicitly implemented methods. It also would not change the type
hierarchy. For example:
>
> ```cs
> public interface IAllGroupsCollector : ICollector // NOTE: we'd need to
keep this interface
> {
> int GroupCount { get; }
> object GetGroup(int index);
> }
> ```
>
> We would then explicitly implement the interface for the GetGroup method.
I think that would be cleaner than exposing a non-generic ICollection, although
I could be swayed on explicitly implementing that too. Thoughts?
That is the reason why I said "abstraction" rather than "abstract class",
because either would probably be fine. I just used abstract classes in most
other places because it meant keeping the same syntax as the upstream code. For
example:
#### Java
```java
List<AbstractAllGroupsCollector> collectors = new ArrayList();
ValueSource vs = new BytesRefFieldSource(groupField);
collectors.add(new FunctionAllGroupHeadsCollector(vs, new Map(),
sortWithinGroup));
collectors.add(TermAllGroupHeadsCollector.Create(groupField,
sortWithinGroup));
```
#### C#
```c#
List<AbstractAllGroupsCollector> collectors = new
List<AbstractAllGroupsCollector>();
ValueSource vs = new BytesRefFieldSource(groupField);
collectors.Add(new FunctionAllGroupHeadsCollector(vs, new Hashtable(),
sortWithinGroup));
collectors.Add(TermAllGroupHeadsCollector.Create(groupField,
sortWithinGroup));
```
Where if we used an interface, it would be declared a bit different:
```c#
List<IAllGroupsCollector> collectors = new List<IAllGroupsCollector>();
```
The main reason for doing this in other places was for being able to access
constants and fields without having to specify the non-generic type (in which
case the syntax stays the same as upstream).
It would also allow the use of the `Collector` abstract class, so the user
could cast to that type just as they could in Java. Being that there are no
methods on `ICollector` that wouldn't also exist on `Collector`, that is not
such a big deal, though.
That being said, using an interface does have an advantage we may be able to
take advantage of: it can be declared internal and used just for testing
purposes. If we use an abstract class, it will have to be declared public in
order for a public type to inherit it. Although, I am not entirely convinced
that getting the `GroupCount` without having strongly-typed access to the
groups is not a valid use case.
After mulling this over a bit more, if the user needed to create a
collection of mixed collectors, they could just declare it `object` or
`ICollector` and then pass it through a set of cast tests upon usage. However,
they would need to know all possible types that it could be cast to at compile
time, which might not be possible for every use case.
```c#
//List<AbstractGroupingSearch> collectors = new
List<AbstractGroupingSearch>();
List<object> collectors = new List<object>();
collectors.Add(GroupingSearch.ByField(...));
collectors.Add(GroupingSearch.ByFunction<MutableValueStr>(...));
collectors.Add(GroupingSearch.ByFunction<MutableValueInt32>(...));
collectors.Add(GroupingSearch.ByDocBlock<string>(...));
collectors.Add(GroupingSearch.ByDocBlock<int>(...));
foreach (object collector in collectors)
{
if (collector is FieldGroupingSearch fieldGroupingSearch)
// do something with the collector
else if (collector is FunctionGroupingSearch<MutableValueStr>
functionOnString)
// do something with the collector
else if (collector is FunctionGroupingSearch<MutableValueInt32>
functionOnInt32)
// do something with the collector
else if (collector is DocBlockGroupingSearch<string> docBlockOnString)
// do something with the collector
else if (collector is DocBlockGroupingSearch<int> docBlockOnInt32)
// do something with the collector
}
```
This would be very cumbersome if all they want to do is read a field that is
common among all of these types and even moreso if they wanted to do that for
any custom subclass (which would probably require reflection).
I am not opposed to keeping `ICollector`. The only thing that is kind of
smelly about it is that there is one constant that is declared on `Collector`
because interfaces in c# don't support constants. So, we need the `Collector`
class whether it is made abstract or not. Since it is currently a static class,
users could be confused that they cannot cast a collector type to `Collector`
as is the case in Java.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]