3.4 KiB
C# Language Design Notes for Apr 5, 2017
Raw notes, yet to be cleaned up - read at your own peril
interface I1
{
void M1() { ... } // non-virtual
virtual void M2() { ... }
}
interface I2
{
void M1() { ... }
sealed void M2() { ... } // non-virtual
}
interface I3
{
virtual void M1() { ... }
sealed void M2() { ... } // non-virtual
}
Do we want to allow non-virtual declarations in interfaces? In practice you probably at least want private methods. But it's likely you'll want internal, and maybe public.
For traits based programming, you'd want to have the same things available, when you "port things over" to a trait.
Intuitively it feels against the spirit of interfaces.
Should an interface be required to put abstract
in order to allow derived interfaces to override them? No. So it probably also shouldn't require virtual
.
So it's the "virtualness" that's implicit, not specifically the "abstractness".
The core motivator is the defaultness. So that is the thing we should design around.
Motivators:
- Defaults are useful!
- Java and Swift
- Traits
Is the copy-paste sceario between interfaces and classes important? It already is very far from possible. property decl in interface today is auto-prop in class today!
itf I
{
sealed void M() { ... }
void N();
void O() { ... }
}
cls C : I
{
pub void M() => base(I).M();
}
Not requiring a modifier for non-virtual is dangerous: I go add a body, and nothing breaks. No implementors break either, as long as they are implementing implicitly. But then it means something different!
Decision 1: non-virtual should be explicitly expressed through sealed
or private
.
Decision 2: sealed
is the keyword to make interface instance members with bodies non-virtual
Decision 3: We want to allow all modifiers in interfaces
Decision n: Default accessibility for interface members is public, including nested types
Decision n+1: private function members in interfaces are implicitly sealed, and sealed is not permitted on them. Private classes can be sealed, and that means sealed in the class sense of sealed.
Absent a good proposal, partial is still not allowed on interfaces or their members.
tuple names
new { X = X, Y }; // {X,Y}
Projection initializer.
Action y = () => {}
var t = (x: x, y) // (int x, Action y) or (int x, Action)?
t.y(); // a call of the second component or of an extension method y?
(int, int) t = (x: x, y); // warning for x. No warning for y, because inferred
We dropped the feature because we didn't have partially named tuples, but now we do.
Options around breaking change:
- We don't want the feature
- We want it but it's too late
- We want it but it has to be now
- We want it and we don't care when - it's esoteric
We go for 2. Due to the forward breaking change it needs to be tied to a language version. Same rules as anonymous types: picked up from simple names, dotted names and ?.'ed names.
Let's aim for 7.1.
tuple names in constraints
We think you could never declare a class (in C#) that satisfies the constraints (in C#) in the examples. You could construct things in IL to circumvent, but it's ok for us not to deal with that. So it's maybe ok for C# not emit an error here.
We do try to emit an error when constraints conflict. But the value is low and it is hard and esoteric and makes the compiler slower.