[Discussion]: Extensions #8696
Replies: 381 comments 12 replies
-
I prefer using public extension Foo of int : IA, IB, IC, ...
{
...
} Otherwise it will be too confusing if you are extending an interface: public extension Foo : IA, IB, IC { } vs public extension Foo of IA : IB, IC { }
|
Beta Was this translation helpful? Give feedback.
-
I'm curious as to how the team weighs the relative benefits between "roles" and "extension implementation". It feels that without some additional effort in the runtime the two are somewhat incompatible with each other, so if those differences can't be reconciled which of the features might the team lean towards? Personally, I find extension implementation much more exciting than roles, but that's just my opinion. |
Beta Was this translation helpful? Give feedback.
-
@hez2010 public extension Foo for IA : IB, IC { } |
Beta Was this translation helpful? Give feedback.
-
Who gave you an early preview of my notes? They're up now, discussion at #5500. |
Beta Was this translation helpful? Give feedback.
-
Here's a scenario that will be great fun to try to accommodate in the design: interface IFoo { }
interface IBar { }
class Thing { }
public extension FooThing for Thing : IFoo { }
public extension BarThing for Thing : IBar { }
void Frob<T>(T t) where T : IFoo, IBar { }
Frob(new Thing()); On an unrelated bikeshedding note, what about using the existing reserved keywords |
Beta Was this translation helpful? Give feedback.
-
@sab39 Given, as you've mentioned, how similar these two concepts are. I too am looking for a good syntactic way to convey that similarity, with a clear way to do indicate in which way they differ. Thanks for the |
Beta Was this translation helpful? Give feedback.
-
I'm not sure if I should re-post my comments from the discussion here?
This is complicated, but doable using current constraints of the framework. An anonymous type can be generated: class <mangled>Thing_IFoo_IBar : IFoo, IBar
{
internal <mangled>Thing_IFoo_IBar(Thing thing) { this._thing = thing; }
readonly Thing _thing;
void IFoo.Foo() { ... } // these member(s) are copied from, or call into, FooThing
void IBar.Bar() { ... } // these member(s) are copied from, or call into, BarThing
}
Frob(new <mangled>Thing_IFoo_IBar(new Thing())); The same can be done for generic types, etc. Yes, it's complicated, but unlike roles, it's very possible. |
Beta Was this translation helpful? Give feedback.
-
This was just one example. It's not the main motivation. We discussed in the LDM that there were definitely plenty of scenarios where you'd still want adapters in a strongly typed way that would be sensible. |
Beta Was this translation helpful? Give feedback.
-
@TahirAhmadov That works, more or less, for the specific example I gave, but what if |
Beta Was this translation helpful? Give feedback.
-
If it's not the main motivation, surely it shouldn't be the one discussed in the OP, should it? |
Beta Was this translation helpful? Give feedback.
-
The OP is simply showing a demonstration. This is a broad topic and we need to spend a ton more time on it prior to even getting close to a place where we could write something up that was fully fleshed out and chock full of examples and whatnot. |
Beta Was this translation helpful? Give feedback.
-
The |
Beta Was this translation helpful? Give feedback.
-
Back with .NET Framework, I've often ran into situations where i wanted a The only thing I don't quite get is why we need two keywords here, |
Beta Was this translation helpful? Give feedback.
-
That's the thing, it would be very interesting to see an example which would demonstrate how |
Beta Was this translation helpful? Give feedback.
-
That's fine. It's something we're working on at this moment '-). The point was raised and was something we intend to get to and write more on. I def don't want us to get the impression that it's just for that. Thanks! |
Beta Was this translation helpful? Give feedback.
-
@HaloFour The reasons provided for the current abomination would be rectified by this new proposal. |
Beta Was this translation helpful? Give feedback.
-
Add some generics. |
Beta Was this translation helpful? Give feedback.
-
public extension<TSource> Enumerable(IEnumerable<TSource> source)
{
public bool All(Func<TSource, bool> predicate);
} |
Beta Was this translation helpful? Give feedback.
-
That's a possibility. I have suggested that whatever syntax could be applied in different scopes, from a single member, to groups of members, to all of the members declared within a type, etc. A separate clause also has the advantage of being declared within another member, and I, personally, think that extension implementation could be compelling for allowing hyper-localized extensions that could capture state. Only supporting it at the type level comes with the issue of having to declare potentially many types to capture the design space we already have. Is that better? That's extremely subjective. I don't find a level of indentation to be onerous, the IDE is doing that for me anyway. |
Beta Was this translation helpful? Give feedback.
-
This appears to just be bike shedding on collapsing syntactic forms. Something virtually every design member I've heard from seems fine with. As I've mentioned many times now. We are not doing the bikeshedding portion currently. We are deciding on core capabilities and semantics. Syntactic pleasantries come last. We have to start though with a STRAWMAN syntax that clearly delineates all the capabilities we want and answers all the questions on every complex case without confusion (like how do different generics merge) |
Beta Was this translation helpful? Give feedback.
-
And as I've mentioned, my feeling is that virtually all of the ldm is on board with that. But it's not an area of focus now because it doesn't change anything fundamentally. That's just syntactic sugar for later. We need to be dealing with the vet hard problems around semantics, compat, etc. first. Also, personally, I very much want this. For example, I definitely have extensions and non-extensions side by side today. Being forced to group these separately is a downside for me. So while I am totally fine with higher level grouping constructs, I still want to be able to apply things at an individual (or small group) level. |
Beta Was this translation helpful? Give feedback.
-
I'm confused by something in the recent Unified Extensions proposal: (#8665) Given this sample: public static class NullableExtensions
{
extension([NotNullWhen(false)] string? text)
{
public bool IsNullOrEmpty => text is null or [];
}
} It seems odd to me to have nullability attributes on the reciever rather than on the method. I would have expected something more like: public static class NullableExtensions
{
extension(string? text)
{
[this: NotNullWhen(false)]
public bool IsNullOrEmpty => text is null or [];
}
} Otherwise, are there additional restrictions on the methods inside the extension scope? What would the following mean, or would it become a warning/error? public static class NullableExtensions
{
extension([NotNullWhen(false)] string? text)
{
public object Foo => throw null!; // doesn't return a bool
public void Foo => throw null!; // doesn't return anything
}
} |
Beta Was this translation helpful? Give feedback.
-
This exactly matches existing extension methods. The return value tells you the state of the receiver value on exit. |
Beta Was this translation helpful? Give feedback.
-
Yes, but on current extension methods you would put it on a method that returns bool, not on a scope containing methods which may or may not return bool. Though with that said, there is nothing stopping you from writing the following at the moment: using System.Diagnostics.CodeAnalysis;
public static class C
{
public static void M([NotNullWhen(false)] this object? x) {
throw null!;
}
} But it does also seem odd to me to force users of the new extension syntax to group methods by their nullability state, rather than any other logical grouping. |
Beta Was this translation helpful? Give feedback.
-
Or something like this public static class NullableExtensions
{
extension([NotNullWhen(false)] string? text)
{
public bool IsNull => return this == null;
public bool IsNotNull => return this != null;
}
} Now what I think something like this is much better (if there actually was public static class NullableExtensions
{
extension(string? text)
{
[this: NotNullWhen(false)]
public bool IsNull => return this == null;
[this: NotNullWhen(true)]
public bool IsNotNull => return this != null;
}
} |
Beta Was this translation helpful? Give feedback.
-
Thinking about the top-level syntax, we can use: public extension<T>(IEnumerable<T> source) as Enumerable
{
// ....
} advantages:
examples: public partial extension<T>(IEnumerable<T> source) as Enumerable where T : IFoo
{
// ...
}
public partial extension<T>(IEnumerable<T> source) as Enumerable where T : IBar
{
// ...
}
public partial extension(IEnumerable<int> source) as Enumerable
{
// ...
}
public partial extension(IEnumerable<long> source) as Enumerable
{
// ...
}
public partial extension(string? source) as Enumerable
{
// ...
} instead of public static class Enumerable
{
extension<T>(IEnumerable<T> source) where T : IFoo { ... }
extension<T>(IEnumerable<T> source) where T : IBar { ... }
extension(IEnumerable<int> source) { ... }
extension(IEnumerable<long> source) { ... }
extension(string? source) { ... }
} |
Beta Was this translation helpful? Give feedback.
-
Hi, I recently found the following code example here: public static class MyExtensions
{
extension<T>(IEnumerable<T> source)
{
public bool IsEmpty => !source.Any();
public IEnumerable<T> Where(Func<T, bool> predicate)
=> new (source.Where(predicate));
public T this[int index] => source.ElementAt(index);
public static IEnumerable<T> Empty => [];
public static implicit operator ReadOnlySpan<T>(IEnumerable<T> sequence)
=> sequence.ToArray().AsSpan();
}
extension(IEnumerable<int> source)
{
public IEnumerable(int element, int count)
=> Enumerable.Repeat(element, count);
public static IEnumerable<int> Range(int start, int count)
=> Enumerable.Range(start, count);
public static IEnumerable<int> operator +(IEnumerable<int> sequence, int value)
=> sequence.Select(i => i + value);
}
} Interesting, what does mean |
Beta Was this translation helpful? Give feedback.
-
It looks like the extensions grammar as currently defined does not permit static fields or const fields. Is this intended? Is it possibly something that could come along in the future? The reason I ask is that the extensions feature may satisfy the use cases for people who want partial enums. But without being able to use const fields, the "extension enum members" won't be usable in attributes and so on. Probably static properties would have to be used instead. |
Beta Was this translation helpful? Give feedback.
-
someone help me i am pretty sure I disabled this thread notifications at least 4 times along these years |
Beta Was this translation helpful? Give feedback.
-
Can a new keyword be extracted from the static class? |
Beta Was this translation helpful? Give feedback.
-
Discussed in #5496
Originally posted by MadsTorgersen November 30, 2021
https://github.com/dotnet/csharplang/blob/main/proposals/extensions.md
Beta Was this translation helpful? Give feedback.
All reactions