Open gafter opened 7 years ago
I wanted to add one simple to understand scenario in higher level code where this would have been a great feature to have on the library level.
We wanted to support SQL row value syntax in Entity Framework Core (https://github.com/dotnet/efcore/issues/26822). Currently, this is the most readable C# syntax the user will have to write to represent row value:
.Where(b => EF.Functions.GreaterThan(new[] { b.Column1, b.Column2 }, new[] { 1, 2 }))
Not so readable.
This proposal would in theory allow providing custom tuple operators and allow the user to write the following instead:
.Where(b => (b.Column1, b.Column2) > (1, 2))
Extension properties would greatly help in dotnet/comet, which heavily uses builder pattern to create views.
new ShapeView(new Circle()
.Fill(LinearGradient)
.Style(DrawingStyle.Fill)).Frame(200,100),
With extensions
new ShapeView(new Circle { Fill = LinearGradient, Style = DrawingStyle.Fill }) { Frame = (200, 100) },
// Frame have signature: void set_Frame(this ShapeView view, (double, double) size)
new Text("TEST PADDING").Frame(height:30).Margin(top:100),
With extensions
new Text("TEST PADDING") { FrameHeight = 30, MarginTop = 100, },
I find extension property version more readable, especially in first case
Extension static methods would be useful for classes in testing/mocking frameworks and elsewhere that define fluent methods hanging off a static class like It
or Is
or Arg
etc. Specifically, when you'd like to be able to add an extra method for something custom that works the same way, slightly prettier than making a MyIt
class or using some fallback that takes a lambda.
@uecasm was just coming to call out the same thing. Being able to write extensions off of Assert
for MS Test for example would be useful for folks to write extensions that flow naturally into an existing test framework.
Is there anywhere to see if any progress is being made on this issue?
Yup. Right here.
Doesn't #5497 supersede this?
That one's a non-starter for me because it requires an extra keyword/decoration on the class being extended. The whole point of extension methods is to extend things that the original designer didn't think of and didn't make extensible any other way.
That one's a non-starter for me because it requires an extra keyword/decoration on the class being extended. The whole point of extension methods is to extend things that the original designer didn't think of and didn't make extensible any other way.
@uecasm Do you mean #5497? I think it does not…
From the proposal
public class DataObject
{
public DataObject this[string member] { get; set; }
public string AsString() { }
public IEnumerable<DataObject> AsEnumerable();
public int ID { get; }
}
gets extended by
public extension JsonDataObject : DataObject
{
public string ToJson() { … this … }
public static DataObject FromJson(string json) { … }
}
I don't see any special keyword. I havn't read the howl thread, so maybe I skiped something
Oh, maybe I overlooked something then. It looked to me like you could only extend role
s.
No, as I understood, Roles are a construct to add Interfaces to external types (and maybe more), but are not nessesary to extend something.
Omg, no ETA for this best feature for C#? 😞
What I'm hoping for with extension properties specifically is something like
public static class MyStringExtensions {
public static string Author {
get(this string);
set(this string);
}
public static string Half {
get(this string baseStr) => baseStr.Substring(baseStr.Length / 2);
}
}
The examples aren't stellar, but I feel like the syntax feels natural and intuitive given what devs are used to with extension methods.
@brianjlacy
Still very much in design, but the current thought is that you declare an "extension type", which can support most members:
https://github.com/dotnet/csharplang/issues/5497 https://github.com/dotnet/csharplang/blob/main/proposals/extensions.md
implicit extension MyStringExtensions for string {
public string Half {
get => Substring(Length / 2);
}
}
I still prefer classic extension syntax, but there's some problems:
public extension MyGuidExtensions for System.Guid // name or qualified name
{
public string ToBase64()
{
// this = treated as Guid instance, internal/private fields should
// be visible only in same project or solution or
// else use reflection instead.
// but... "this" can be omitted? this is a good question
return Convert.ToBase64String(this.ToByteArray());
}
}
public extension DynIntExtensions for Int32 // force qualified name instead keyword?
{
public int SevenBitLength
{
get
{
int numBytes = 0;
var value = this; // make a copy from self, but how?
do
{
value >>>= 7;
numBytes++;
}
while(value > 0);
return numBytes;
}
}
}
// Sample code:
var offset = 0;
int packetId = reader.ReadVarInt32();
var bytesToSkip = packetId.SevenBitLength;
offset += bytesToSkip
Or something similar to what the Rust language does with the impl
keyword and traits Rust: Traits & Impl
@nathan130200 i'm not quite certain i'm getting what the problems are that you're referring to.
@nathan130200
// name or qualified name // force qualified name instead keyword?
I would expect that you could use the name (or alias) of any type in scope, including the keywords for the primitive types like int
.
// this = treated as Guid instance
My gut feeling is that within an "extension" type that this
would refer to the extended type, e.g. System.Guid
, and not the extension itself.
var value = this; // make a copy from self, but how?
If the above is true, then I would expect that this would just work.
These are good questions for the extensions working group.
@CyrusNajmabadi
It's not really a problem but the comments I put in the code. Some things don't seem so objective. For example, "this being treated as an instance of Guid".
@HaloFour
My gut feeling is that within an "extension" type that this would refer to the extended type, e.g. System.Guid, and not the extension itself.
So in this case, the compiler could understand that if we are inside the extension, it will treat this
as if it were the instance of the object itself.
Or better yet, add a keywork called self
that refers to the object we are extending, leaving this
for the extension itself, because the extension would not be a class or a struct, it would just be a way of extending the fields, methods , properties and events, or can we consider the "extension" type as a reference/value type?
I think that these extensions are just a way of instructing the compilation and not a class in itself. Because what happens if you want to cast the extension? I don't think it would make sense to cast to a type that doesn't actually inherit, it's just "inheritance" at runtime, I think that's the right word.
I think that these extensions are just a way of instructing the compiler/IDE and not a class in itself. Because what happens if you want to cast the extension? I don't think it would make sense to cast to a type that doesn't actually inherit, it's just "inheritance" at runtime, I think that's the right word.
Ah, unless it is implemented, there is some way to do an implicit/explicit cast for the extension.
"this being treated as an instance of Guid".
I'm not seeing the issue. Why would that not be hte case?
Or better yet, add a keywork called self that refers to the object we are extending, leaving this for the extension itself,
I don't see the value of that. if self
refers to the object being extended... then the current extension still applies to it, since the extension applies to all instances of the extended type.
or can we consider the "extension" type as a reference/value type?
Neither? The extension is just a set of members on a particular type. That partial type can be any type (unless we choose to limit choices there in some fashion).
It's like asking "is an extension method a reference or value type?". It's neither. It's a method. An "extension" then is just a collection of those members (all extending a particular type), and it's not limited to just methods. It can be things like constructors, properties, etc.
As an example, ignoring that your code has a property, you could imagine that:
public extension DynIntExtensions for Int32 // force qualified name instead keyword?
{
public int SevenBitLength
{
get
{
int numBytes = 0;
var value = this; // make a copy from self, but how?
do
{
value >>>= 7;
numBytes++;
}
while(value > 0);
return numBytes;
}
}
}
Is just shorthand for:
public static class DynIntExtensions
{
public int GetSevenBitLength(this int @this)
{
int numBytes = 0;
var value = @this;
do
{
value >>>= 7;
numBytes++;
}
while(value > 0);
return numBytes;
}
}
All that 'implicit extensions' (as a feature) is doing is:
What could be done in this case is:
In the case of fields, events and properties, perhaps an attribute that makes the compiler identify that that extension "method" can be invoked as a member.
Just like you used GetSevenBitLength
in the example, add an attribute [ExtendedMember]
, [ExtendedProperty]
or something better to identify.
You can't do extension fields as extensions have no way to add state to anything.
Allow static types to receive these extensions members as well.
They can.
perhaps an attribute that makes the compiler identify that that extension "method" can be invoked as a member.
That's what an extension already does. See how extension methods are encoded into a dll. It's just an attribute that marks then as such.
or something better to identify
We don't need that in source as the source already indicates this by use of the extension
keyword.
You can't do extension fields as extensions have no way to add state to anything.
The "workaround" would be an extension property returning a ref-value, which is "almost as good" as a field. It can lookup or create a container based on the instance so it has someplace to store the data, e.g. a ConditionalWeakTable, and then can return a reference into that data container.
See also https://github.com/dotnet/roslyn/issues/11159