morelinq / MoreLINQ

Extensions to LINQ to Objects
https://morelinq.github.io/
Apache License 2.0
3.67k stars 411 forks source link

Ease usage in next major version by renaming methods conflicting with Enumerable methods #565

Closed voxoid0 closed 2 years ago

voxoid0 commented 5 years ago

For historical reasons, some of MoreLinq's method names conflict with System.Linq.Enumerable's, causing compiler errors like the following from Issue 527:

The call is ambiguous between the following methods or properties: 'System.Linq.Enumerable.Append(System.Collections.Generic.IEnumerable, TSource)' and 'MoreLinq.MoreEnumerable.Append(System.Collections.Generic.IEnumerable, T)'

This can often be resolved by using a static import of one of MoreLinq's *Extension classes, such as MoreLinq.Extensions.AppendExtension.

However, having to manually do a static import rather than being able to continue to use tooling's auto-import (such as provided by Visual Studio or ReSharper), while it makes a work-around possible, is still a big pain and a slow-down. Furthermore it seems that some cases of using methods from both Linq and MoreLinq in the same file are only resolvable by calling the extension method from the static class name directly, rather than using it as an extension of IEnumerable. These problems are said to be rare, but I have ran into it several times just in the past few weeks (perhaps because I do a lot of functional-style C# programming...which is exactly why I love MoreLinq!)

For this reason I propose that in the next major version of MoreLinq, the conflicting method names be renamed where they may cause this conflict. (The same thing could happen again in the future if more Enumerable methods are added to System.Linq, but at least the conflicts will be reduced, and for a while exhaustively.)

atifaziz commented 5 years ago

Below is a copy of relevant comments from #527 for context.


@voxoid0 wrote:

Having to manually do a static import rather than being able to continue to use tooling's auto-import, while it makes a work-around possible, is still a big pain and a slow-down. Furthermore it seems that some cases of using methods from both Linq and MoreLinq are only resolvable by calling the extension method from the static class name directly, rather than using it as an extension of IEnumerable.

For this reason I propose that in the next major version of MoreLinq, the conflicting method names be renamed where they may cause this conflict.

@atifaziz wrote:

some cases of using methods from both Linq and MoreLinq are only resolvable by calling the extension method from the static class name directly

@voxoid0 Do you have examples of those cases?

For this reason I propose that in the next major version of MoreLinq, the conflicting method names be renamed where they may cause this conflict.

This will be a never-ending chase because the conflicts can occur with any library, not just between System.Linq and MoreLinq. The problem needed solving in a generlised manner and it took many (elapsed) years of thinking and labour (see #214 and #235) to come up with and especially commit to an acceptable solution. It might be a pain and slow to do the static imports but I believe anyone would prefer that safety any day over potential present & future conflicts, as well as breaking changes. Perhaps and eventually, this is an issue to be taken up with the C# design team rather than libraries with extensions. Meanwhile, I propose instead to fix this via tooling if someone has the itch and the time. This can be done, for example, as a C# analyser for the IDE. It could be, I think, made to be MoreLINQ-specific or not.

@voxoid0 wrote:

It is indeed a difficult problem. Do you know of specific other libraries that also create conflicts? I haven't ran into any other conflicts beside with System.Linq. Also the *Extension solution still doesn't prevent having to sometimes call the extension method directly from the static class name, which disrupts the functional flow for which we use Linq/MoreLinq. While conflicts can occur with other libraries, it seems far rarer than the conflicts with System.Linq which we can more easily track. Heh, this definitely depends on each programmer's perspective, habits, preferences as to which solution is most desirable. And like myself, we probably all feel strongly one way or another :P . I like to be able to write functional code without having to stop and assign a variable from a static class method call, or go to the top of the file and type a static import and hope I won't have to still call a static class method directly; this is more valuable to me than having to occasionally update my MoreLinq code if I decide to upgrade to the next major/breaking version. It'd be interesting to know where the votes would favor. I'm willing to accept if most people prefer the current solution; but I'd sure like the no-conflicts solution to have a chance.

atifaziz commented 5 years ago

Furthermore it seems that some cases of using methods from both Linq and MoreLinq in the same file are only resolvable by calling the extension method from the static class name directly

You still haven't called out these cases specifically. I'm curious to learn why I haven't experienced the same trouble.

robertsynoradzki commented 5 years ago

Let me throw my three cents worth into this discussion.

While I understand the approach to keep this library conflict-safe from other unknown amount of libraries in the wild, I think you should stop and think about these totally unproven statistics:

  1. 100% of users unhappy with MoreLinq being conflicted with Append/Prepend/System.Linq are using MoreLinq.
  2. 99.(9)% of those users must surely also be using System.Linq at the same time.
  3. An unknown fraction of this number could be using more LINQ libraries and/or obsolete .NET versions, causing inevitable conflicts.

I see you are trying to make your library safe to use with any and every .NET version and LINQ libraries and while this is honourable, I am unhappy in being in the 99.(9)% group that also must fallback to static imports, which are a nightmare in MoreLinq because:

  1. Once you add your first static import and remove using MoreLinq, Intellisense will stop suggesting available operators.
  2. It ruins the flow of typing queries.
  3. Usually when a conflict occurs in a file, you already have used several MoreLinq operators, so having been faced with necessity to first change them all to static imports before being able to continue does not provoke a smile.
  4. It is not supported in LINQPad, which is the best tool to test LINQ queries, but suddenly MoreLinq is so hard to test I catch myself thinking about library betrayal.

If I may, please let me try to convince you to sacrifice uttermost compatibility with infinite past and future, to instead make your 99.(9)% user group happy and make their life easy again, as it was in MoreLinq 2.x. The fallback to static imports seems a fine solution for the rest of them.

P.S. Maybe - although I have no experience in it and it may be impossible - some conditional compilation targets could be configured, where MoreLinq 3 could be targeting several .NET versions, conflict-free with each thanks to #if directives?

atifaziz commented 5 years ago

think about these totally unproven statistics

@ensisnoctis So here's the other side of the coin I'm looking at and where we do have some statistics we can debate on. At the time of writing this, the MoreLINQ package has been downloaded ~3.5M times. Version 3.0, where static imports were introduced, is the second highest downloaded version at over ½M downloads. The only other two versions in the same neighbourhood are versions 2.10 and 1.4 (550,073 and 581,644 downloads, respectively). Given those, the number of people who have come screaming and kicking about this issue are far shy of 10. It tells one of two things about the rest: they are either happy and have accepted the state of affairs as being a fair trade-off or they are all blissfully unaware of the pain and storm that awaits them because they haven't yet upgraded to a version of .NET where conflicts exist.

please let me try to convince you to sacrifice uttermost compatibility with infinite past and future, to instead make your 99.(9)% user group happy and make their life easy again

So I'm afraid I'm not convinced yet but I haven't closed this issue precisely for the reason that I am waiting for the hoards of folks that I hear will be coming in with their pitchforks. The issue is right there for anyone to convey their horror though I'd rather see a fruitful discussion with people proposing (thought out and objective) solutions and contributing ideas rather than just express their discontentment.

static imports, which are a nightmare in MoreLinq because:

Let's talk about your nightmare now.

4. It is not supported in LINQPad, which is the best tool to test LINQ queries, but suddenly MoreLinq is so hard to test I catch myself thinking about library betrayal.

So there are two ways around this. Setup a query with all the static imports and a reference to MoreLINQ and save it. Next time you need to work or experiment with MoreLINQ in LINQPad, open that query and select New Query, same properties from the File menu, or just press Ctrl+Shift+N as a short-cut.

Alternatively, create a code snippet like the following:

Put that into your snippets directory and then type morelinq followed by Tab in any query to get all your static imports and MoreLINQ references added magically. The only unfortunate thing is that LINQPad's import clean-up function is buggy as it removes those static imports whenever you ask for a clean-up but this is something to be taken up with @albahari and LINQPad's support.

You can do something similar with code snippets in Visual Studio. Import the following code snippet:

You do that by usually putting it in "Code Snippets\Visual C#\My Code Snippets" under the Visual Studio version folder in your documents folder. Then whenever you need those static imports, press Ctrl+K followed by Ctrl+X to insert the snippet. Use all the extension methods you need (they'll be all discoverable and available at this point) and finally press Ctrl+R followed by Ctrl+G to get rid of those static imports that you never used; rinse and repeat.

This takes care of the following point as well:

  1. Once you add your first static import and remove using MoreLinq, Intellisense will stop suggesting available operators.

As for:

3. Usually when a conflict occurs in a file, you already have used several MoreLinq operators, so having been faced with necessity to first change them all to static imports before being able to continue does not provoke a smile.

Again, drop the MoreLinq import, insert the static imports via the code snippet and then ask Visual Studio to remove the unused ones. This will take 10 seconds. 🚀

2. It ruins the flow of typing queries.

Code snippets can be inserted while in the middle of your typing and the imports will go in the right location at the top of the file without breaking your flow.

In closing, I'd like to repeat the following:

Perhaps and eventually, this is an issue to be taken up with the C# design team rather than libraries with extensions. Meanwhile, I propose instead to fix this via tooling if someone has the itch and the time. This can be done, for example, as a C# analyser for the IDE. It could be, I think, made to be MoreLINQ-specific or not.

So while an analyzer would be cool and perhaps more involved, I hope I've demonstrated that code snippets can be used as a lightweight alternative to alleviate some of the pain.

NetMage commented 5 years ago

Or perhaps we just decide using MoreLINQ isn't worth it since it conflicts with System.Linq and never start.

dzmitry-lahoda commented 5 years ago

What it the summary of approach chosen to tackle the issue? I fully support the idea that MoreLINQ should not conflict with corefx, but it is fine to conflict with other libraries.

voxoid0 commented 5 years ago

Furthermore it seems that some cases of using methods from both Linq and MoreLinq in the same file are only resolvable by calling the extension method from the static class name directly

You still haven't called out these cases specifically. I'm curious to learn why I haven't experienced the same trouble.

Hi atifaziz,

I ran into the case again today so thought I'd share. In this specific case, I have a class in which I'm using System.Linq's Take(), and MoreLinq's Slice(), and therefore have imported both System.Linq and MoreLinq.Extensions (or just MoreLinq results in the same problem). Then I go to use TakeLast(), and that's where the ambiguity error occurs. What can I do to solve it? The easiest way to resolve this that I know of is to scroll to the top of the file, and modify using MoreLinq; to using static MoreLinq.Extensions.SliceExtension;, and then continue to manually add other such static usings for each additional MoreLinq extension method I wish to use, including ones that do not conflict with System.Linq (since otherwise I would need the using MoreLinq statement again which would bring back the ambiguities. A lot of people, probably most, are used to not disrupting coding flow by going to the top and manually adding using's, instead using auto-completion for that. So it would make a very noticeable difference in usability.

I would also add that, while maybe only 10 people have reported this issue, keep in mind that only a small fraction of people finding trouble like this will take the time to go report the issue or join a discussion. If 1 out of 100 people do that, then you have maybe 1000 people running into it, and also these are most likely your main/biggest users, because they're using a variety of MoreLinq calls within the same class.

atifaziz commented 5 years ago

I ran into the case again today so thought I'd share.

@voxoid0 Thanks for taking the time to do that.

What can I do to solve it? The easiest way to resolve this that I know of is to scroll to the top of the file, and modify using MoreLinq; to using static MoreLinq.Extensions.SliceExtension;, and then continue to manually add other such static usings for each additional MoreLinq extension method I wish to use, including ones that do not conflict with System.Linq…A lot of people, probably most, are used to not disrupting coding flow by going to the top and manually adding using's, instead using auto-completion for that. So it would make a very noticeable difference in usability.

Did you read about the quick-fix solutions I offered with code snippets earlier? That's the only easy way I know of without much investment on anyone's end, although it did cost me time to think, investigate and share that option.

atifaziz commented 5 years ago

but it is fine to conflict with other libraries.

@dzmitry-lahoda Why is it fine?

atifaziz commented 4 years ago

LINQPad 6 added a neat feature that allows one query to reference another using a #load directive. FWIW, I've created a new repository that, once cloned, makes MoreLINQ as easy as adding the following line on top of your LINQPad query:

#load "MoreLINQ\load\MoreLinq.linq"

And you shouldn't run into conflicts (due to explicit and static imports) with same named LINQ extensions in .NET Core 3.

kcragin commented 4 years ago

@voxoid0 @atifaziz

Thanks for this discussion.

...keep in mind that only a small fraction of people finding trouble like this will take the time to go report the issue or join a discussion. If 1 out of 100 people do that, then you have maybe 1000 people running into it...

Here's another 1000 users. :) I have refrained from commenting for a while. I am a little perplexed about the stance of not dropping overlapping methods from a major release. The tooling (e.g. VS and ReSharper) don't do a good job of disambiguating these, and the manual intervention, does break flow.

Might it be worth it to introduce a MoreLinq.Core that does not have overlapping extensions (and possibly experimental extensions for that matter)? That preserves the MoreLinq package in its current form.

Just a thought...

Crossbow78 commented 4 years ago

To me it would make sense to avoid publishing ambiguities with the library you're "extending" (System.Linq), especially when there's tooling available exactly for that purpose.

Resorting to snippets feels like a clunky work-around where the problem is moved to the users of this library, as they need to be configured and require some maintenance for library updates. I'm having a hard time recommending this library to our junior developers when it needs all these additional side-notes on its usage. And with future .NET versions, we'll probably only run into more and more conflicts.

Just imagine the powerful experience if you could always simply use the extension you're looking for and the MoreLinq library would only supplement the missing ones for the .NET version you're currently targeting. And also when 'upgrading' your project to a later .NET version, you would not have to deal with all these ambiguities that suddenly pop up.

I'm not sure yet what it would look like exactly, but would the author be open to a PR that implements a multi-target build? I'd be happy to give it a go.

brunoalfirevic commented 4 years ago

I just had to solve similar issue in one of my own utility packages, which has ToHashSet extension method. That method was introduced to System.Linq.Enumerable in .NET Standard 2.1.

Simply removing or renaming the method and doing a multi-target build doesn't work properly. The problem are transitive dependencies to the utility package.

Let's say you have XmlParsingUtils package which dependes on CollectionUtils package (which has ToHashSet extension method). CollectionUtils has multi-target build implemented, for .NET Standard 2.0 and .NET Standard 2.1 targets. The 2.1 target renames the offending ToHashSet method. Additionally, XmlParsingUtils package doesn't implement multi-target build because it doesn't care about any changes between .NET Standard 2.0 and 2.1.

Now, let's say you have the application targeting .NET Core 3.1 which references XmlParsingUtils package. The build process for your application will ultimately resolve CollectionUtils package which targets .NET Standard 2.1 and things will break at runtime with System.MissingMethodException when the code from XmlParsingUtils package tries to call the method it expects to be there.

Possible solution: What seems to work correctly is using multi-targeting and conditional compilation to make ToHashSet method regular (non-extension) method for .NET Standard 2.1 target. That way, the runtime can correctly link all the methods used. Furthermore, users referencing CollectionUtils package who target .NET Standard 2.1 or higher will will not get extension method resolution conflicts because for them (or rather, their compiler), ToHashSet is not extension method at all.

This makes for a nice user experience if your extension method has the same semantics as the one from the framework.

jbrantly commented 4 years ago

Normally I wouldn't make a 👍 comment, but since the maintainer explicitly said he's waiting for the hordes of users to complain about this, I figured I'd give my feedback. I didn't add MoreLinq to the codebase, but I'm making a change and ran into a conflict with TakeLast, and it saddens me that I had to spend time on this. I'm surprised by the stance that conflicting with System.Linq when importing MoreLinq is "OK". I'm sure it's annoying to always be playing catchup with additions to System.Linq, but that's the nature of the beast when making a library that extends something upstream, and this stance is punishing users who want to use the latest .NET bits.

gandhis1 commented 4 years ago

Add me to this "horde". I concur with others who believe that using static is not a long-term solution. Deprecation and/or disambiguation is.

R2D221 commented 4 years ago

I have this problem as well. I know that whenever I need to convert an enumerable to a hash set (ToHashSet(), of course), it's game over. I have to go remove the using MoreLinq; line, fix the ToHashSet() invocation, and then just see how badly my code broke because half of it was depending on a MoreLinq method. And since I don't retain the full list of methods in my head, whenever I want to make a query with a new method, I have to come to the browser, search the MoreLinq documentation and see if a method fits what I'm trying to do, and then go back to Visual Studio to add the resepective using static to finally placate the compiler.

bradleypatton commented 4 years ago

Just found out about MoreLinq today from a StackOverflow question about finding the max object in a list. Library looked interesting and so I added it to my project and immediately ran into this issue with a conflict with TakeLast. For a library that extends System.Linq I would expect it to sunset or rename conflicting methods as new versions are released. Perhaps a different package named MoreLinq.Core (as someone else suggested) but the static approach seems sub-optimal.

TipluSebastian commented 4 years ago

Add me to this "horde". I concur with others who believe that using static is not a long-term solution. Deprecation and/or disambiguation is.

Same for me.

viceroypenguin commented 4 years ago

All, I have submitted PR #749 which would alleviate these issues. We don't need to rename methods or hide them. We simply have to remove the this additive so that they are no longer extension methods in newer frameworks. Then there is no compile-time conflict, but any run-time dependencies still exist if code was compiled on an earlier framework and run on a newer framework.

kcragin commented 3 years ago

The number of methods overlapping with Enumerable is growing, which means the frustration is growing. I fully support this project, and I can understand the reluctance (maintenance issue) of removing methods, but when it's so many, this burden is shifted from the few maintainers to the many consumers, which seriously mars the developer experience.

Just a few of the methods I've had issues with: Pipe(), Prepend(), Append(), ToHashSet(), TakeLast().

Think of it this way: the project was born (I assume) out of a need/want to fill in the gaps in the original LINQ library. If those gaps are being filled, then the duplicates in Morelinq just become a burden.

Of the hundreds of OS packages/libraries I've used I can't think of any other project with this issue.

thomaslevesque commented 3 years ago

I had similar issues in a similar project. When this happens, I just introduce new TFMs in the package, without the offending methods. Obviously, that doesn't address the binary compatibility issue, but I don't think it really matters; who upgrades packages without recompiling?

viceroypenguin commented 3 years ago

@thomaslevesque - the binary compatibility is not due to concerns about the host project. As you stated, many people recompile when upgrading a library, and obviously recompile when they change a TFM.

Binary compatibility is important due to other dependent projects. Consider the case: MyProject -> ThirdParty -> Linq.Extras, while MyProject also takes a direct dependency on Linq.Extras. If ThirdParty is built against the 4.6 TFM and is never rebuilt, then it will have a binary dependency on the Prepend() existing in Linq.Extras. However, because MyProject is upgraded to the 5.0 TFM, it relies on a version of Linq.Extras that does not include Prepend(). Then you find a runtime crash MethodNotFoundException when ThirdParty attempts to make the call to Prepend().

thomaslevesque commented 3 years ago

@viceroypenguin very good point. I didn't think of that.

aeb-dev commented 3 years ago

Add me to the horde. Thank you TakeLast.

Zofware commented 3 years ago

I'm abandoning use of MoreLinq in my project due to this issue.

viceroypenguin commented 3 years ago

@Zofware FYI I have released morelinq.temp with the fix in #749 until it is merged to mainline and released.

MatthewSteeples commented 2 years ago

With .NET 6's release just around the corner, there are a few more extension methods that Enumerable is providing. The 2 that I'm interested in here are MayBy and MinBy. In contrast to other conflicts, I would prefer to carry on using the MoreLinq version of these, as it returns a set of objects with the max/min value, rather than a single arbitrary object.

I'm struggling to construct the right using statements so that I can use the right MaxBy but not conflict with the LINQ namespaces. Any help would be appreciated:


using System;
using System.Linq;
using static MoreLinq.Extensions.MinByExtension;

var collection = new[] { "one", "two", "three" };

var sorted = collection.OrderBy(a => a.Length);
var minBy = collection.MinBy(a => a.Length); //Gives a CS0121 error saying it's ambiguous between MinBy on Enumerable and MinBy on MinByExtension

Best option I've come up with so far is to not call it as an extension method, but just a regular static method

viceroypenguin commented 2 years ago

If you switch to morelinq.temp package, I have addressed .net 6.0 conflicts, along with the other conflicts in earlier versions of .net (core).

I have submitted a PR back to this package to address this, and will deprecate my package once it gets approved, but this will work for now.

MatthewSteeples commented 2 years ago

I don't think that PR solves for the scenario I'm looking at. Your tests check that you can compile new int[0].MaxBy(x => 1);, but the problem is that the return value type is different: in MoreLinq an enumerable of objects is returned, whereas in .NET6 a single entity is returned. It's the behaviour of MoreLinq that I would like to use

viceroypenguin commented 2 years ago

Ah, right. Sorry. I didn't even pay attention to that (in .net or in your comment). I'll take a look. Probably requires renaming the function in .net 6 version of the library (as a breaking change).

MatthewSteeples commented 2 years ago

Yes, I think that the way I'm going to get round it is to "wrap" the extension method in my own extension method (eg MaxBySet) in a different class, so that one will only reference MoreLinq and not System.Linq

viceroypenguin commented 2 years ago

@MatthewSteeples - I have updated according. I chose .MaxElementsBy() as a naming for the method. I have published this to nuget under version 4.0.0 (since this is a breaking change). Let me know if this helps!

mthgr commented 2 years ago

I've run in this same issue using DistinctBy (net 6) and unless I'm missing something the only real solution is using it as a static method. I've read the Usage section and a clear example would probably help if I'm missing something. Maybe it's over simplistic but why not rename the conflicting methods to DistinctByMore and add be done with this...

viceroypenguin commented 2 years ago

@mthgr Renaming the existing method would be both a source and binary break for existing consumers of the method. Not doing so would be the preference, especially when the same source can be written against both the MoreLinq version and the Microsoft version.

I tried the following code, while referenced to morelinq.temp, under netcoreapp3.1, net5.0, and net6.0, and it was able to compile and run just fine:

using MoreLinq;

var list = Enumerable.Range(1, 100);
var distinct = list.DistinctBy(x => x % 10);

Can you please share the code that does not work with this?

mthgr commented 2 years ago

Sorry for the late reply.

I referenced morelinq.temp in my project. I removed the regular morelinq package I was using (let me know if I'm missing something).

This is a different example but it's the same problem (with MinBy this time).

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Threading;
using MoreLinq.Extensions;
using MoreLinq;

private static void Resolve()
{
       string keyToUpdate = _statusDictStore.Statuses.Where(x => !x.Value.Resolved)
            .MinBy(kvp => kvp.Value.MessageServerReception).First().Key;
 }

I'm getting Ambiguous invocation: MoreLinq.IExternalEnumerable

Program.cs(984, 18): [CS0121] The call is ambiguous between the following methods or properties: 'System.Linq.Enumerable.MinBy<TSource, TKey>(System.Collections.Generic.IEnumerable, System.Func<TSource, TKey>)' and 'MoreLinq.Extensions.MinByExtension.MinBy<TSource, TKey>(System.Collections.Generic.IEnumerable, System.Func<TSource, TKey>)'

Please let me know if I'm missing something...?

viceroypenguin commented 2 years ago

Oh, yes. You should remove the using MoreLinq.Extensions; line. That version still has a conflict with the .net version. Using only the using MoreLinq; and using System.Linq; should have no conflicts.

mthgr commented 2 years ago

If I comment out using MoreLinq.Extensions; the code seems to be using the Microsoft version for MinBy...?

viceroypenguin commented 2 years ago

Correct. Since there is a practical difference between the Microsoft and the MoreLinq version of MinBy, I renamed the MoreLinq one to MinElementsBy() in morelinq.temp

mthgr commented 2 years ago

Excellent. Thank you for your help. Will the temp version be merged eventually?

viceroypenguin commented 2 years ago

The maintainers have been silent on the issue for well over a year, so I suspect not. I have not yet decided what to do about that.

robertsynoradzki commented 2 years ago

One option is to stay independent of a 3rd party library and implement the methods you need yourself. You could search the open source if you need a guidance on how to do that, but remember that there is learning and growing in implementing them yourself.

At least that's the direction I've decided to take. I've opted out from recommending to my work colleagues a library that has more and more conflicts with .NET with each passing year that it refuses to solve properly. Besides, LINQ is getting more and more useful methods in native .NET anyways, an external library is becoming less mandatory.

P.S. I'm sorry to post this negative opinion here. I appreciate and support open source, but I cannot shed the feeling of disappointment with how this issue was mishandled.

viceroypenguin commented 2 years ago

I have released a preview version of a fork SuperLinq, which I will keep updated with newer .NET versions. (https://www.nuget.org/packages/SuperLinq/)

atifaziz commented 2 years ago

I apologise for the complete radio silence (priorities, COVID & life that happens when you're busy making plans), but I think it's time to conclude this issue so that the suspense doesn't kill anyone. 😁

I have been keeping an eye on the comments and I am quite sure to disappoint everyone here (without it being my intention) by saying that I haven't found any compelling arguments that make it worth my time beyond what was done (with thought, care and tremendous effort) to avoid conflicts in the first place. This issue has been open for a long time, so I hope you don't find this to be an act of impulse. I have also not seen any input from other long-time/substantial contributors to MoreLINQ and there's been plenty of time for that, so for better or worse, I am stepping in to make the call. Some of you have expressed abandonment of MoreLINQ and many more may take the same course right after this conclusion, and that's perfectly okay.

I would personally like to thank @viceroypenguin for taking the time and effort to have gone the distance and provided a solution with some solid thinking behind it. It's unfortunate that in spite of the cleverness of his solution (that I seriously contemplated adopting at one time), it had to eventually resort to renaming of methods to avoid conflicts with identically named but behaviourally different methods that have cropped up with more recent versions of .NET. This is not a chase I'm willing to do, but I am glad that @viceroypenguin is publishing SuperLinq and that the community has an alternative.

All that said, some methods have 100% compatibility in name and behaviour, like ToHashSet, and may be safe to drop from the next major version when breaking changes are fair to introduce.


PS Please bear in mind that many people have dedicated enormous amounts of time maintaining MoreLINQ since well over a decade so be kind, thoughtful and respectful in your reactions should you feel the need to offer them.

MahnevMaxim commented 1 year ago
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using MoreLinq;

namespace ExampleCodeGenApp.Extensions
{
    public static class MoreLinqNoConflictExtensions
    {
        public static IExtremaEnumerable<T> MinByMoreLinq<T, TKey>(this IEnumerable<T> collection, Func<T, TKey> func)
        {
            return collection.MinBy(func);
        }

        public static IExtremaEnumerable<T> MaxByMoreLinq<T, TKey>(this IEnumerable<T> collection, Func<T, TKey> func)
        {
            return collection.MaxBy(func);
        }
    }
}