-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Issues in Default Interface Methods #406
Comments
For the Another idea (maybe already raised by others) is to actually put the class stuff into a class, e.g.: interface I
{
void M();
default class C : I
{
public virtual void M() { ... }
}
} So no need for a lot of new rules to learn and remember. |
For var s = default(S);
s.M(); // error: 'S' does not contain a member 'M' to work. But if it is hard, I can also live with |
I've added more open issues to the OP, starting with "Base interface invocations" |
|
The question is confusing (or I am too tired), but I think it's supposed to say "with an implementation". I think requiring an explicit modifier on all default methods is the better option. A "plain old method" would then be Requiring a modifier forces the programmer to make an explicit choice. |
That syntax feels very Java. Maybe |
Added some resolutions from today's LDM, including Closed Issue: Should a concrete method (with implementation) be implicitly virtual? [YES] |
I like the syntax |
|
Wouldn't that just cause a stack overflow, if it's virtual? |
@alrz Because you want to invoke the base interface's default implementation rather than the current override implementation. |
Nevermind, apparently it doesn't work that way. I see base(T) has been chosen cause it works like default(T). That's great. |
Regarding base interface invocation, I'm not a fan
Regarding diamond inheritance
I don't think that the second rule is useful. It's better to require derived class to implement explicit override. Regarding Binary Compatibility issues, they are very similar to concerns raised in #409. Following existing C# logic for base invocation, Binary Compatibility 1 should (although probably unintuitively) run |
Oops, wrong thread. Moved to #52 (comment) |
@zippec
That would make it an incompatible change to add default methods to interfaces, because it would change the behavior of this program interface IA
{
void M();
}
interface IB : IA
{
}
class Base : IA
{
void IA.M() { WriteLine("Base"); }
}
class Derived : Base, IB // allowed?
{
static void Main()
{
Ia a = new Derived();
a.M();
}
} when a default method implementation is added like this interface IA
{
void M();
}
interface IB : IA
{
override void M() { WriteLine("IB"); }
}
class Base : IA
{
void IA.M() { WriteLine("Base"); }
}
class Derived : Base, IB // allowed?
{
static void Main()
{
Ia a = new Derived();
a.M(); // what does it do?
}
} Since we don't want the addition of default methods to an interface to affect the meaning of existing correct programs that do not use default methods, we need the second rule. See the open question "Diamond inheritance and classes". |
The following draft notes from 2017-04-19 LDM have been partially reflected in the open issue list and specification: DIM Event accessorsToday, syntactically, either both or neither accessor can have an implementation. Should we allow just one to be specified? Overridden? ConclusionIf you have only one, you probably have a bug. Let's not allow it. Not blocked for future idiots to ... DIM ReabstractionYes, adding a body to an interface member declaration shouldn't break C. DIM Sealed overrideShould it be allowed? would it prevent overrides in classes or only in interfaces? It seems odd to prevent either. Also, it is weird in connection with diamond inheritance: what if one branch is sealed? ConclusionLet's not allowed DIM sealed members (not on list)Some folks in the community find it weird, and that they look too much like things that can be implemented in classes. ConclusionWe think it is going to be useful, but will come back to it. This is a mental model tripping block. DIM not quite implementing a member (not int list)You have a member and implement an interface. The interface adds a new member with a default implementation, that looks like your method but doesn't quite make it an implementation. Bug? Intentional? We can't provide a warning, because it would assume it was a bug. ConclusionCan't do anything about this. DIM implementing inaccessible interface members (not in list)The way the runtime works today, a class member can happily implement an interface member that isn't accessible! That's not likely to be dependent on today (no language will generate that interface), but we need to decide what semantics to have here. We could continue to only have public members in interfaces be virtual. But if we want protected, internal and private, we should probably have it so they can only be implemented by classes that can see them. But this means that interfaces can prevent other assemblies from implementing them! This may be a nice feature - it allows closed sets of implementations. ConclusionThis is still open, but our current stake in the ground is we should allow non-public virtual interface members, but disallow overriding or implementing them in places where they are not accessible. DIM Implementing a non-public interface member (not in list)Would we allow them to be implemented implicitly? If so, what is required of the accessibility of the implementing method?:
ConclusionFor now, let's not allow any of these. We can relax as we think through it. |
I have a question about unfortunate interaction with default members vs structs: How this (will) work with generics constrained to interface with default members? Lets me show example:
The thing is, as far as i understand, generic constraints avoid boxing on value types and because of that default members should work flawlessly. But i would like to be sure. If thats true then i vote for 2->3 ------------------------------>1 in that order (from best to worst) because default members will still be useful on structs even if you decide to leave it as-is (without forbidding nor providing code generator). Otherwise i still vote on 2 but 3 and 1 become effectively same. |
With regard to non-public overrides, isn't this what the strict modifier in IL is supposed to control? I was under the impression that without that any inaccessible method could be overridden at the IL level. |
That is the essence of the open question. Inside the interface's default method, the @MI3Guy I don't know anything about that. If your point is that our choice on how this works may affect the IL we elect to emit, I believe that. |
I can't say that I've ever actually heard anyone talk about the strict Method Attribute, but it shows up in the CLR spec (II.15.4.2.2, not sure if there's a better reference) and that's how I understood what it is supposed to do. |
There are many things extension method would not have a problem with For instance. default method for struct could make it as generic constraint interface public static class Ext
{
public static void M<T>(this T obj) where T : ISomeThing
{}
}
struct S : ISomeThing {}
S s;
s.M(); // not boxed |
Regarding base interface member access, what's wrong with this? interface I0
{
void M() { Console.WriteLine("I0"); }
}
interface I1 : I0
{
override void M() { Console.WriteLine("I1"); }
}
interface I2 : I0
{
override void M() { Console.WriteLine("I2"); }
}
interface I3 : I1, I2
{
// an explicit override that invoke's a base interface's default method
void I0.M() { I2.M(); }
}
|
@paulomorgado I read in a design note that |
Not as a part of the DIM proposal. The methods are not virtual, cannot be overridden and would require an implementation. They'd be invoked as Type-classes, shapes, extension interfaces, whatever, etc., may eventually provide a way to define static methods that need to be implemented by a target type, but they are still under design. |
From this document: https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md it looks like Preview 5 and VS16.2 Preview 1 already have all the features implemented. |
See #2485
|
Thank you @ufcpp the information is fragmented and I didn't see that.
But it looks horrible to me :) Thanks |
Will protected interface methods be allowed, but unusable, in C# 8? |
I'm hitting the same thing. I'd expect them to just be in scope and also to be unambiguously callable via |
My specific use case looks like this: public interface ICloneable<T> where T : ICloneable<T>
{
T Clone();
}
// the assembly that contains the domain logic
public interface IFooState : ICloneable<IFooState>
{
public string FooID {get; set;}
public Bar Bar {get; set;}
protected void CopyTo(IFooState newState)
{
newState.FooID = this.FooID;
newState.Bar = Bar;
}
}
// a different assembly that persists or proxies FooState objects
public class FooState : SomeBaseClass, IFooState
{
public string FooID {get; set;}
public Bar Bar {get; set;}
public IFooState Clone() => ((IFooState)this).CopyTo(new FooState());
} |
There is no public interface I1
{
protected void M1() {}
}
public class C : I1 {
public void M()
{
M1Helper(this);
}
private static void M1Helper<T>(T self) where T: C, I1
{
self.M1(); // this works
}
}
|
Well you'd best get a move on and add it. The whole world (well me at least, speaking on behalf of the whole world without consulting them) is expecting a completed v8 to be released next month along with Core 3.0, so we are all going to be bitterly disappointed when this doesn't happen 😉 |
@DavidArno see #406 (comment) P.S. I don't work in the team and am interested in this feature as well, as you may have read in this thread. |
Core 3.0 will not have runtime changes necessary to support the |
All of the open language issues described here were resolved in https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-03-27.md and I have updated the issue list above to show the resolution. |
Reopening so a spec can be updated with these resolutions. |
@gafter Your workaround works because there is no M1 declared in class C. |
@raffaeler No, there isn't. We are working on a possible addition to C# in 9.0 that would permit it. See #2337. Until then you are out of luck unless you can modify the source to expose the functionality, perhaps as a static or sealed method. |
@orthoxerox It looks like they are allowed, but are unusable. I can declare an Shouldn't that result in a compiler error if it can't be used? |
While I miss the base() feature, if you use them for implementing traits, the current implementation is enough. |
@raffaeler I was tinkering around with it, and the restriction that properties can either have a public setter, or no setter is a bit of damper on traits. To be clear, the compiler allows for it, but the implementing class can't seem to implement the property. Traits would be a godsend for something like DDD, but at the same time setters need to be private or protected, and the traits methods should be able to set public properties on the trait itself. But without a setter, the default method implementations cannot. Overall, this is exciting, but there are some oddities between what the compiler allows and what actually functions. Which has led to a few disappointments. |
@douglasg14b From various comments on this repo and on the Roslyn one I understand that this feature is just the beginning to open to traits topic (which is very wide). |
All of these individual issues are closed (decided by LDM). When it has been confirmed that they are reflected in the specification this github issue can be closed. |
These have been reflected in the spec, with the exception of |
The following notes are intended to prime the pump for LDM discussion as soon as the LDM has time to take up these issues.
Issues in Default Interface Methods
protected
,private protected
, andprotected internal
access (closed)We tentatively decided to permit
protected
members in interfaces, admitting we need to specify the meaning ofprotected
,internal
, and related access modes. The draft spec permits these but does not describe the meaning. We also need to ensure the runtime agrees to implement them, if needed.We may need to use
protected
members in interfaces for explicit interface implementations so that we can generate code forbase(T).M()
that has access to those explicit implementations: the only way to ensure that we invoke an accessible method is to make such methodsprotected
from the IL point of view.Here is what runtime spec was saying about accessibility of interface members prior to this feature:
We were pretty comfortable with allowing all accessibility attributes that do not include protected access because those were pretty well understood and do not depend on inheritance relationship between types:
As it turns out the runtime specification for the meaning of inheritance explicitly describes its meaning for interfaces:
This provides a clear meaning for
protected
in the runtime specification, which we could mirror in the language specification.Resolution: We will support these protection levels in interfaces. A protected member in an interface is accessible in all interfaces that derive from it. A protected member in
object
is not accessible in an interface. A protected member in an interface is accessible through abase(I)
qualifier in a class or struct type that implements the interface.reabstraction (closed)
We specified our intent to permit reabstraction (See https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md#reabstraction and https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-03-21.md#reabstraction) and it is required for Java interop (See https://grokbase.com/t/openjdk/lambda-dev/11cvy137rq/what-does-reabstraction-mean) which is one of our key scenarios. Consequently the only specified form for reabstraction is
The
abstract
modifier is currently explicitly forbidden for explicit implementations. Do we intend to also (for clarity) permit something likeOr is the former form sufficient?
We also need to ensure the runtime agrees to implement reabstraction, if needed.
Resolution: We allow reabstraction, and the
abstract
modifier is required (and permitted) on an abstract implementation. https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-03-27.md#reabstractionexplicit interface abstract overrides in classes (closed)
Should we support explicit interface abstract overrides in classes?
Resolution: No. A class must implement every interface method, directly or indirectly. It may, as ever, do so using an abstract method of the class. https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-03-27.md#explicit-interface-abstract-overrides-in-classes
Is
object.MemberwiseClone()
accessible in an interface? (closed)Resolution: No. Protected members of
object
are not accessible in interfaces. https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-03-27.md#is-objectmemberwiseclone-accessible-in-an-interfaceConfirm:
static int P { get; set; }
is an auto-property with a compiler-generated backing field? (closed)Resolution: Yes. https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-03-27.md#is-static-int-p--get-set--is-an-auto-property-with-a-compiler-generated-backing-field
Confirm:
partial
on a method declaration impliesprivate
, and no access modifier is permitted? (closed)The existing language specification says that partial methods are implicitly
private
and that no access modifier is permitted. This item is for the LDM to either confirm that position when they are declared in an interface, permitprivate
on them, requireprivate
on them, or something else.Resolution: Yes, confirmed. https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-03-27.md#confirm-that-partial-implies-private-and-no-access-modifier-is-permitted
static fields (closed)
Open Issue: Are static fields and the like permitted in interfaces?
Resolution: Yes.
operators (closed)
Closed Issue: Should operator declarations be permitted in an interface? Probably not conversion operators, but what about others?
Decision: See https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-06-27.md for details.
new (closed)
Closed Issue: Should
new
be permitted on interface member declarations that hide members from base interfaces?Resolution:
new
is supported on interface members for a long time. It simply suppresses a warning. See https://github.com/dotnet/csharplang/blob/master/spec/interfaces.md#interface-methodsconst (closed)
Open Issue: Should
const
declarations be permitted in an interface?Resolution: Yes.
System.Runtime.CompilerServices.RuntimeFeature.DefaultInterfaceImplementation (closed)
Is that the best name for the CLR feature? The CLR feature does much more than just that (e.g. relaxes protection constraints, supports overrides in interfaces, etc). Perhaps it should be called something like "concrete methods in interfaces", or "traits"?
As it stands right now the name of the member is
DefaultImplementationsOfInterfaces
.Decision: The LDM doesn't care what its name is.
base(Type).Member
for class types (closed)Open Issue: Confirm
base(Type).Member
permitted for class types.We think we approved using the new
base(Type)
syntax whereType
is a class type (e.g. to skip a base and invoke your base's base), but we should explicitly confirm that. Also we should confirm thatbase(Type).M()
might refer to a non-virtual memberM
. Also we should confirm that there is an accessibility requirement: theM
found by this lookup must be accessible where the invocation occurs (i.e. the usual name-lookup constraint).Decision: Yes.
The text was updated successfully, but these errors were encountered: