New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add default interface methods to IEnumerable<T> and friends #29513
Comments
Despite my irrational, bitter hatred of DIM. This is useful and very clever. I am a little worried about default no-op |
We feel that the risk vs reward for this particular interface doing a DIM to provide a base member isn't justified. We're open to experimentation, but not quite this interface...at this time. |
While just as risky I believe the reward for using a DIM in #31001 is far greater and would be worth it. |
@bartonjs I'm wondering what the risks are for doing this? |
The general form of the risk of using DIMs to provide base implementations is the introduction of "the diamond problem", which can manifest itself as a recompile break (existing code fails to compile simply by upgrading to a newer version), and possibly also runtime failures. I'm having trouble finding, offhand, where we'd be likely to introduce the diamond internally here, so I'll have to have to introduce a random external interface... but that's actually the place where the biggest risk to the community-at-large comes in. So, let's pretend that on the first version that supported DIMs (3.1?) I made this gem: public interface ITypedEnumerable : IEnumerable
{
TypedEnumerator GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public class TypedEnumerator : IEnumerator
{
...
} To complicate matters, let's also add another interface, which could be in the same package/assembly or a downstream one: public interface IMoarInterfaces : ITypedEnumerable, IList<Whatever>
{
...
} So then .NET N comes along and has But what does the runtime do? When loading Those are the complications we've thought of. Part of the problem with DIMs is we haven't used them, so we don't actually know when they introduce more problems than they solve; but this one seems to have at least "some" risk while only replacing a very easy line of boilerplate, so it doesn't pass the risk/reward evaluation. Diamonds are avoided if you never use DIMs for base implementations, just for aliasing or pure default (throw, false, zero, whatever) implementations. public interface ISomeNewInterface
{
// Maybe some implementation wants to pick a different encoding than UTF8,
// but now there's "with the object's default encoding".
// Or maybe it can benefit from not needing to do the null check on Encoding.
//
// No matter what, simplification overloads are now less cumbersome on interfaces,
// and don't have the extension method problem of requiring strong type references
// to replace.
//
// But we haven't learned all the versioning risks yet, so do it on the first
// version the type was introduced and don't split-compile to a version without it :)
virtual object DoSomething(string s) => DoSomething(s, Encoding.UTF8);
object DoSomething(string s, Encoding encoding);
} |
The interfaces
IEnumerable<T>
andIEnumerator<T>
are ideal candidates for some of the existing interface methods to gain default implementations since many of the non-genericIEnumerable
/IEnumerator
methods simply wrap the generic versions and box the return value.Proposal below. I may have gotten the syntax a bit wrong but you get the gist. :)
This would make it easier for devs writing custom enumerators since they'd need to write less boilerplate code. They could always choose to override the default implementations if desired, but these defaults would likely work for the 99% use case.
Hat tip to @agocke, who brainstormed this.
The text was updated successfully, but these errors were encountered: