[Open issue]: static abstract interfaces and static classes #9122
Replies: 57 comments
-
I like this with one caveat. If the interface has no static members, you still get an error. So this would include an empty interface, as well as an interface with only instance-DIM methods. Alternatively, we could say: interface needs to be non-empty and only contain static members to be implementable by a static class. |
Beta Was this translation helpful? Give feedback.
-
That seems reasonable. |
Beta Was this translation helpful? Give feedback.
-
Could even be a requirement to add the static modifier to the interface declaration? |
Beta Was this translation helpful? Give feedback.
-
I believe that's the option (2) I outlined 😄 |
Beta Was this translation helpful? Give feedback.
-
For option 2, it seems like an unnecessary bifurcation to me, unless regular types can also implement so-called |
Beta Was this translation helpful? Give feedback.
-
Yes, they'd be able to. You'd just only be able to implement an interface on a static class if the interface was also static, but making the interface static wouldn't require that all implementors be static. |
Beta Was this translation helpful? Give feedback.
-
I see. In that case, my gut reaction would lean towards option 2 for the intentionality of the design: otherwise, we introduce another way that you can silently break your users 😅 |
Beta Was this translation helpful? Give feedback.
-
Considering that static classes cannot be used as generic type arguments today, how is this feature useful? Without the ability to do that, you cannot actually use an interface as an abstraction for a static class. This feature alone would not really get you anywhere. |
Beta Was this translation helpful? Give feedback.
-
2 does seem like the better option here, allowing |
Beta Was this translation helpful? Give feedback.
-
I don't understand the point. Consider: (I'd also like the option of being able to put static on structs to get the same validation, but that's introducing a new concept and is a separate discussion.) |
Beta Was this translation helpful? Give feedback.
-
This feature does make sense to have along with static abstracts in interfaces, but I agree that as is, the feature alone has no utility.
If the implementing types were static, as this feature would enable, you would no longer be able to use them as generic type arguments, due to error CS0718. To do this with a static class, the language would also have to be modified to permit static types to be passed as generic type arguments, most likely along with a new type of constraint to indicate what type parameters may be static and prevent the generic code from declaring a variable, field, or parameter of these type arguments. |
Beta Was this translation helpful? Give feedback.
-
Yes
Maybe. To me the utility of a static class isn't that it prevents me from declaring a useless instance (there are many other ways to achieve that), it's that as the creator of the type it helps me avoid problems by adding members to a type that shouldn't be there. But if we went with option 2, then the language could permit static classes as generic arguments if the generic parameter was constrained to a static interface, and it could also ensure that variables, fields, etc. of such statics weren't created. |
Beta Was this translation helpful? Give feedback.
-
I would vote for option 2. I think the intention part of it is important just for readability, but also for future maintenance - if somebody tries adding an instance member to that static interface, they are stopped from doing so immediately, and while yes they can just remove the static keyword, at least it's something that they have to do manually - there was a set of eyes who makes that decision. On the other hand, with option 1, an instance member is added, this project is built, then another project which relies on this project fails downstream all of a sudden. |
Beta Was this translation helpful? Give feedback.
-
Well... that's what I get for reading these proposals on my phone... my bad... |
Beta Was this translation helpful? Give feedback.
-
🍝 Would it be plausible to also allow I admit I haven't thought up a use-case for it, it's just something that came to mind. |
Beta Was this translation helpful? Give feedback.
-
Extremely unlikely. |
Beta Was this translation helpful? Give feedback.
-
I really like this proposal, but I'd like to add one additional thought around option 2. Since a Therefore, wouldn't it be possible for the C# implementation to be shape-based, like TypeScript interfaces rather than traditional C# interfaces. This would allow any class that implements all of the static methods to be considered as matching a generic constraint on the interface, not just classes that explicitly implement the interface. This could be useful in cases where the static method signatures are matched by a 3rd party library not under the developer's control. |
Beta Was this translation helpful? Give feedback.
-
If anti-constraints like |
Beta Was this translation helpful? Give feedback.
-
@ds5678 I think I'd rather just allow static types to be passed as type parameters everywhere. We already have a very similar situation: public abstract class BasicallyStatic
{
private BasicallyStatic() { }
}
new List<BasicallyStatic>() // Where's the danger in this? To the runtime, there aren't static types, only abstract sealed types. |
Beta Was this translation helpful? Give feedback.
-
If a static class can implement an interface, it seems interesting to allow a static class to inherit another static class. static class Shape : IShape { ... }
static class Circle : Shape, ICircle { ... } |
Beta Was this translation helpful? Give feedback.
-
@jnm2 It does have inherent flexibility and brevity. Those are large benefits that cannot be ignored. However, it changes the mental model for what a static class is. ActuallyStatic? local = null;
List<ActuallyStatic?> list = [ local ]; Previously, locals and expressions could not be typed as a static class because such classes could never have instances. Now, we would have to choose between making these methods uncallable or allowing static classes to be a valid target type. Using an anti-constraint would indicate intent. The compiler could issue errors when a developer attempts to use the generic type argument in an invalid way. |
Beta Was this translation helpful? Give feedback.
-
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. |
Beta Was this translation helpful? Give feedback.
-
Would that need to change? You can coax the runtime to create a My concern is that by adding |
Beta Was this translation helpful? Give feedback.
-
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. |
Beta Was this translation helpful? Give feedback.
-
Why wouldn't an instance class be able to inherit from a static interface? |
Beta Was this translation helpful? Give feedback.
-
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.
It could potentially not be a breaking change.
This is Steve's option 2 in the original proposal. The reason to have a |
Beta Was this translation helpful? Give feedback.
-
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>();
|
Beta Was this translation helpful? Give feedback.
-
This would help tremendously for scenarios where you want to inject an ILogger where T is a static class. |
Beta Was this translation helpful? Give feedback.
-
@molinch Not sure I follow. Can you demonstrate how this would help in that scenario? This doesn't let you do anything you can't already do by just not marking the class static. Usage of the static interface would be the same. |
Beta Was this translation helpful? Give feedback.
-
@mikernet This proposal would allow to define an ILogger where Whatever is a static class. |
Beta Was this translation helpful? Give feedback.
-
Champion issue: #9123
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
Beta Was this translation helpful? Give feedback.
All reactions