Open stephentoub opened 2 years ago
Speaking of breaking changes, I think we should allow static classes to inherit from interfaces with instance members, as long as all those instance members have default implementations. This allows breaking change rules for interfaces to stay the same.
My concern is that by adding static interfaces that can only be implemented by static classes
Why wouldn't an instance class be able to inherit from a static interface?
@ds5678
However, it changes the mental model for what a static class is.
I don't think it'll have a big effect in practice. The compiler can outright tell you you won't be able to use a member with a signature that includes that type parameter when the type parameter is a static class.
Although intriguing, I don't think this should be done because changing a static class to an an instance class is not currently a breaking change.
It could potentially not be a breaking change.
Why wouldn't an instance class be able to inherit from a static interface?
This is Steve's option 2 in the original proposal. The reason to have a static
keyword for interfaces would be to make that be the declaration of intent around which everything hinges, instead of dynamically recognizing that a static class could implement that interface. If this option was taken, it would be following the same rationale as not having method return types be inferred from the return statements in the body. Declarations of intent provide an easy way to see what external usages will be supported and to explicitly notice and decide before changing that support.
Just thought I'd mention some uses that I'd like to be able to write:
static void X1<T>() where T : allows static => ...;
X1<Console>(); //an example type
interface I { /*some static members allowed here*/ } //"static interface?"
static class C : I { }
struct S : I { }
static void X2<T>() where T : allows static, I => ...;
X2<C>();
X2<S>();
X1
would allow for any type that is allowed today + static classes (and ref structs could also be allowed if they're not implicit with another allows
). This would be extra useful for when we get generic bridges, e.g., so that we can bridge to a static interface that we check later.
X2
would allow any type that implements the interface, including static classes (since all the members of the interface are static). I think it would be a massive shame if we disallowed non-static-classes to implement them though, since any type can have static members, and I shouldn't have to move my implementation to another type if I don't think it's appropriate in my situation, just so I can allow a static class to implement it in another situation (nevermind the lack of generic specialisation, which is also an issue that would probably often encourage me to use structs anyway).
With static abstract interface methods, consider:
This compiles fine. However, if I try to make
C
be a static class:it fails to compile with:
Such an error arguably made sense before static abstract interface methods, as interface methods could only be instance members, and static classes can't have instance members (and even for an empty interface, you can't create instances of a static class and thus couldn't even use the interface as a typical marker used with
is
casts).However, with static abstract interface methods, you can have an interface entirely composed of static abstract members, and it would logically make sense to have a static class implement that interface.
We could address this by either:
static interface
such that it can only contain static abstract members and not instance members, and then allow static classes to implement static interfaces.One of the reasons one might want a static class implementing a static interface is to be able to then use that static class as a generic type argument. However, that is also prohibited by the language today, e.g. this:
results in:
We should also consider lifting this constraint.
If we do lift it, then it becomes possible for a generic type or method to create locals, fields, etc. of that generic type, which could be a static class. Those members or locals would then be useless, since you wouldn't have a way to create an instance of them nor invoke anything off of them. If that's something we want to avoid, we could go with option (2) above, allow static classes to be used as a generic type argument when that argument is constrained to a static interface, and then language rules around static types (extended from classes to interfaces) should naturally prevent creating such members and locals.
Design Meetings
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-02-16.md#static-abstract-interfaces-and-static-classes