132 lines
3.9 KiB
Markdown
132 lines
3.9 KiB
Markdown
# C# Language Design Notes for September 10, 2018
|
|
|
|
## Agenda
|
|
|
|
1. Nullability of constraints in inheritance and interface implementation
|
|
2. Static local functions
|
|
|
|
# Discussion
|
|
|
|
QOTD: *Don't open your mind so much the language falls out*
|
|
|
|
## Nullability of constraints in overriding
|
|
|
|
In C# we don't allow override methods to re-specify generic constraints.
|
|
The question then is how to treat constraints that are inherited from
|
|
a base method with a different `NonNullTypes` attribute. For example:
|
|
|
|
```C#
|
|
// Assembly 1
|
|
[NonNullTypes(true)]
|
|
class Base
|
|
{
|
|
virtual void M<T>() where T : C?
|
|
{ }
|
|
}
|
|
|
|
---
|
|
// Assembly 2
|
|
|
|
[NonNullTypes(false)]
|
|
class Derived : Base
|
|
{
|
|
override void M<T>()
|
|
{ }
|
|
}
|
|
```
|
|
|
|
Does using `T` as a non-null type produce a warning, even though we've said
|
|
`[NonNullTypes(false)]` in the derived class?
|
|
|
|
**Conclusion**
|
|
|
|
When the base context is `true` and the derived is `false`, suppress related
|
|
warnings. When the base is false and the derived is true, treat unannotated
|
|
constraints as oblivious.
|
|
|
|
|
|
### Inheritance and explicit interface implementation
|
|
|
|
Similar to the above case, there's a question about how "sticky" nullability
|
|
is through interface implementation and inheritance. For instance, the following
|
|
class uses a constructed inherited member to explicitly implement an interface.
|
|
Does nullability on the type parameters have to match?
|
|
|
|
Here is the correct implementation:
|
|
|
|
```C#
|
|
interface I1<T>
|
|
{
|
|
void M<U>() where U : T
|
|
}
|
|
|
|
class C : I1<C?>
|
|
{
|
|
void I1<C?>.M<X>() {}
|
|
}
|
|
```
|
|
|
|
And what if you remove the question mark in the explicit implementation?
|
|
|
|
```C#
|
|
class C : I1<C?>
|
|
{
|
|
void I1<C>.M<X>() {}
|
|
}
|
|
```
|
|
|
|
**Conclusion**
|
|
|
|
If the member and the implementing type share the same `NonNullTypes` context,
|
|
the annotations must match, so the example directly above that mismatches
|
|
`C?` with `C` would be an error stating that the class `C` does not implement
|
|
the interface `I1<C?>`.
|
|
|
|
If the `NonNullTypes` context differs and the context on the outside is
|
|
`false` and the inside is `true`, there is no error or warning about the
|
|
mismatch, but a warning that `?` is disallowed in `NonNullTypes(false)`
|
|
context. If the context on the inside is `false` and the `outside` is true,
|
|
there is also no error or warning, this time because the interior context
|
|
causes `C` to be treated as oblivious, which can match with `C?`.
|
|
|
|
## Static local functions
|
|
|
|
Proposal: https://github.com/dotnet/csharplang/issues/1565
|
|
|
|
We like this proposal. One potentially confusing part is that static local
|
|
functions cannot capture local variables, even in containing static methods.
|
|
This is appropriate for the design, and we intend to keep it, but acknowledge
|
|
that it may be confusing at first.
|
|
|
|
**Q & A**
|
|
|
|
*Q:* If the proposal is accepted we would allow two states to be enforced: all
|
|
capturing is allowed and no capturing is allowed. Do we want to allow
|
|
intermediate states with something like capture lists?
|
|
|
|
**A:** No, this is as far as we go.
|
|
|
|
*Q:* Should we allow attributes too? That could be useful for related scenarios,
|
|
like P/Invoke.
|
|
|
|
**A:** Yes, we liked the idea originally, but it didn't make C# 7.0. We'd like
|
|
to finalize support for this.
|
|
|
|
*Q:* Should we relax shadowing rules? This isn't strictly related to the
|
|
proposal, but it seems like the restriction is more draconian with static local
|
|
functions because you cannot capture variables and instead have to come up with
|
|
new names if they are being passed as arguments.
|
|
|
|
**A:** We dislike differing shadowing behavior between static and non-static
|
|
local functions. We're warm to the idea of allowing all local function parameters
|
|
shadow locals in parent scopes. We're also interested in allowing shadowing in
|
|
lambdas. We would like to see a separate proposal on this to document the details.
|
|
|
|
*Q:* Can static local functions capture type parameters?
|
|
|
|
**A:** Yes.
|
|
|
|
*Q:* Do we want to allow "static lambdas"?
|
|
|
|
**A:** The value seems much lower since lambdas are usually much shorter. It's
|
|
also a more intrusive syntax inline to the call. Rejected. |