Open gafter opened 5 years ago
Would it be possible to add an example of what would be possible under plan A, but is not under plan B? Thanks
If I understand correctly:
public interface IFoo
{
int M();
}
public interface IBar : IFoo
{
int IFoo.M() => 42;
}
public interface IBaz : IFoo, IBar
{
int X1() => base(IBar).M(); //Plan B
int X2() => base(IFoo).M(); //requires Plan A
}
@Joe4Evr
Was that meant to be IBaz : IBar
?
Not necessarily. Under Plan A, the method implementation could come from somewhere else and would be looked up at runtime. But I'll adjust anyway.
In that case what's the difference between calling base(IFoo).M and call M directly? A case when IBar or IBaz hides M?
@Joe4evr Not quite
public interface I1
{
int M();
}
public interface I2 : I1
{
int I1.M() => 42;
}
public interface I3 : I2
{
}
public interface C : I3
{
int X1() => base(I2).M(); // Plan B
int X2() => base(I3).M(); // requires Plan A
}
Sorry that it might be too late but could we change syntax to base<T>
instead of base(T)
I still feel awkward to have type in parentheses and thinking that syntax related with type should always be <T>
if possible
@HaloFour Are there anywhere I could look for reason behind this decision?
@Joe4evr Thank you very much
Well, actually I wish that typeof
sizeof
and default
should changed to use <T>
too instead or additionally, and we would always use <T>
as a type related argument everywhere
There is a thread starting at https://github.com/dotnet/csharplang/issues/406#issuecomment-495905388 with some users noting how they would really like to use this functionality. I'm posting this here in case it is useful in prioritizing our candidates for C# 9.0.
There is a draft specification for base(T)
at https://github.com/dotnet/csharplang/issues/2910
Prioritization of runtime support for this is being tracked internally at https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1009879
This seems to be especially useful for structs, as calling an explicit interface implementation via a base lookup would remove the need for boxing to the specific interface type, and thus also allow modifications of the original value done by the called method.
@IllidanS4 that is not how interface methods work. They always have a reference-typed this
.
@gafter So you mean DIM will always box struct?
Yes. We tried to design it so it would not, but it would have made things much more complicated.
Thank you, well, yet another reason I should try to avoid DIM as much as possible
@Thaina how do you imagine this boxing would occur without the base(T) feature?
@gafter Or did you mean normal DIM will not box struct, only when we did override DIM and then try to call the original interface DIM?
how do you imagine this boxing would occur without the base(T) feature?
When an interface is used as a type constraint...
@gafter I was referring to this case:
struct Struct : IEnumerable<object>
{
public IEnumerator<object> GetEnumerator()
{
yield break;
}
IEnumerator IEnumerable.GetEnumerator()
{
yield break;
}
public IEnumerator GetNonGenericEnumerator()
{
return ((IEnumerable)this).GetEnumerator(); //boxes
return base(IEnumerable).GetEnumerator(); //calls IEnumerable.GetEnumerator directly
}
}
Seems like that's what should happen, as base(T)
, as I understand it, should call the method on this
but perform the lookup on a base class or interface. At this place, the compiler should know perfectly which method to call, and if that is not the case, it seems to me it would create some confusion, if it is hiding the boxing operation.
Of course one could argue that this code should be designed the other way around, but I've also run into this situation with partial structs where the auto-generated part expected the struct to implement a particular set of generic interfaces, differing only in the return type for one method, I couldn't simply create a new custom-named method for every one of them.
You appear to completely misunderstand what a base call does. It calls the implementation in the named type.
-Neal
From: IllidanS4 notifications@github.com
Sent: Thursday, January 16, 2020 2:21:30 AM
To: dotnet/csharplang csharplang@noreply.github.com
Cc: Neal Gafter Neal.Gafter@microsoft.com; Mention mention@noreply.github.com
Subject: Re: [dotnet/csharplang] Champion: base(T)
phase two (#2337)
@gafterhttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fgafter&data=02%7C01%7Cneal.gafter%40microsoft.com%7C85bf9266e7174e7aebe608d79a6ddb2b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637147668931214022&sdata=g%2Bb2s7KVt%2FbrEm6B04EVuPUMKsHX%2BN8Cl2I%2FJex1wXE%3D&reserved=0 I was referring to this case:
struct Struct : IEnumerable
IEnumerator IEnumerable.GetEnumerator()
{
yield break;
}
public IEnumerator GetNonGenericEnumerator()
{
return ((IEnumerable)this).GetEnumerator(); //boxes
return base(IEnumerable).GetEnumerator(); //calls IEnumerable.GetEnumerator directly
}
}
Seems like that's what should happen, as base(T), as I understand it, should call the method on this but perform the lookup on a base class or interface.
Of course one could argue that this code should be designed the other way around, but I've also ran into this situation with partial structs where the auto-generated part expected the struct to implement a particular set of generic interfaces, differing only in the return type for one method, I couldn't simply create a new custom-named method for every one of them.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fdotnet%2Fcsharplang%2Fissues%2F2337%3Femail_source%3Dnotifications%26email_token%3DAA5AZOWIVMV6AESIADH3W6LQ6AYKVA5CNFSM4G5QM2XKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJDROMQ%23issuecomment-575084338&data=02%7C01%7Cneal.gafter%40microsoft.com%7C85bf9266e7174e7aebe608d79a6ddb2b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637147668931214022&sdata=xZqPRv5Pi6HHTrwVa4CaNjb%2Bi8bioGmzqWY3s1uwYfo%3D&reserved=0, or unsubscribehttps://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAA5AZOVCA5K634PLBSMTYCLQ6AYKVANCNFSM4G5QM2XA&data=02%7C01%7Cneal.gafter%40microsoft.com%7C85bf9266e7174e7aebe608d79a6ddb2b%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C637147668931224014&sdata=STNC93fhU%2BUiWi6CKDGvRmWo1VdC44diQi4%2BH1ZXFkE%3D&reserved=0.
My mistake, I just forgot that interfaces can contain implementation. But still, if the interface doesn't provide the implementation, this construct could be used to refer to the implementation present on the current type, couldn't it?
No
The default interface members proposal had an example that allowed for overriding default implementations:
using static System.Console;
interface IA
{
void M()
{
WriteLine("IA.M");
}
}
interface IB : IA
{
override void IA.M() // The modifier 'override' is not valid for this item
{
WriteLine("IB.M");
}
}
interface IC : IA
{
override void IA.M() // The modifier 'override' is not valid for this item
{
WriteLine("IC.M");
}
}
Currently that results in compiler errors as shown above. Did the ability to override implementaions get cut from C#8 along with base(T)
or are the two on different implementation paths?
@JinShil Drop the override
keyword. I think that should work.
Just a friendly ping on this issue. Looking at the list of upcoming C# 11 features, they aren't nearly as valuable, and even somewhat superficial, compared to the ability to call base default interface methods. Default interface methods will always be incomplete without this feature, and those creating complex hierarchies, and wishing to maximize code reuse, will always run into this limitation. Due to this limitation I have begun using source generators as a substitute. Please finish this implementation.
Any progress on this? I stumbled upon another issue that'd be solved by base invocation: https://github.com/dotnet/csharplang/discussions/8269
Agree that this is badly needed, since could be a massive source of bugs in years to come when interfaces are used without detailed knowledge of their implementation!
/subscribed
On 2019-02-27 the LDM met to make some decisions about the default interface methods feature.
[Plan A]: We decided that the best design for the
base(I).M
feature would be that we lookupM
in the interface typeI
, and the result must be accessible. Moreover, there is a requirement that if the found member is abstract, the typeI
must have a unique most specific (concrete) implementation in the typeI
. The compiler would emit IL that identifies the method found, and the typeI
, and the runtime would select the most specific (concrete) implementation in the typeI
and invoke it (or throw an exception if there is no unique most specific implementation at runtime). There is no IL defined today that would serve this purpose, so we would have to design it. (One option would be to generate a "constrained" prefix with the interface typeI
). The IL should not assume or require that the implementing method is accessible to the caller (e.g. it could be private), but it does require that the declared method named in the IL is accessible.Unfortunately, we do not believe we have the resources to design and implement the feature in this form (plan A) before we'd like to deliver its first implementation (hopefully in C# 8.0). So initially, we'll ship a language feature that is more constrained. This is our plan B for C# 8.0:
[Plan B]: We lookup
M
in the interface typeI
, and the result must be accessible. Moreover, there is a requirement that the found member must have an implementation directly in the typeI
, and that implementation method must be accessible. The compiler will produce IL that directly (non-virtually) invokes that method.We expect to ship plan B in C# 8.0.
This issue is for moving to plan A in a subsequent language version.
Update: LDM 04-29-2019 cut
base()
from C# 8.0.