86 lines
4.7 KiB
Markdown
86 lines
4.7 KiB
Markdown
# C# Language Design Notes for Jun 28, 2017
|
|
|
|
## Agenda
|
|
1. Tuple name round-tripping between C# 6.0 and C# 7.0
|
|
2. Deconstruction without `ValueTuple`
|
|
3. Non-trailing named arguments
|
|
|
|
|
|
# Tuple name round-tripping between C# 6.0 and C# 7.0
|
|
|
|
There are a few unintended breaking changes with tuples that came out through real use cases.
|
|
|
|
Say there's an interface `IUtil`:
|
|
|
|
``` c#
|
|
public interface IUtil
|
|
{
|
|
void M((int a, int b) x);
|
|
}
|
|
```
|
|
|
|
I want to implement this interface with code that works both in C# 6.0, but it turns out I can't! In C# 7.0 we force you to use the same tuple element names when you implement an interface member, but of course you can't do that (and aren't being forced to) in C# 6.0, where your only option is to use `ValueTuple<...>` directly.
|
|
|
|
Maybe using the `TupleName` attribute directly in C# 6.0 would help? It is hard to use, but it turns out VS 2015 happily inserts it for you to match the attribute found on the interface member. Little does it know that this attribute is about to become compiler-reserved in C# 7.0, and if you try to compile the C# 6.0 compliant implementation with the attribute in C# 7.0, it still fails, now complaining that you cannot use that attribute in source code!
|
|
|
|
There are no less than two breaking changes here, so we'll address them one at a time:
|
|
|
|
## Issue 1: C# 6.0 has to implement it without names, but C# 7.0 requires names
|
|
|
|
This will not do. We need to relax the rules in C# 7.0 to allow for a C# 6.0 implementation to continue compiling.
|
|
|
|
A couple of reasonable options:
|
|
- a tuple type *without* names can always be given when one *with* names is required (but one with different names cannot)
|
|
- you are permitted to omit required names only if you use the `ValueTuple<...>` syntax, not with tuple syntax
|
|
|
|
The latter is attractive in that it pushes things to a corner, but it does break the fact that `ValueTuple<int, string>` is exactly equivalent to `(int, string)` in all scenarios.
|
|
|
|
It also has a bit of a problem in establishing whether the tuple syntax *was* used:
|
|
|
|
``` c#
|
|
itf I {
|
|
void M((int a, int b) x);
|
|
}
|
|
|
|
cls Base {
|
|
public void M((int, int) y);
|
|
}
|
|
|
|
// Separate assembly
|
|
cls Derived, I {} // how does it know which syntax Base used?
|
|
```
|
|
|
|
### Conclusion
|
|
Let's go with a strict version of the former option: If a member has *any* tuple element names in it, and is required to match another member (by overriding or implementing), then *all* required tuple element names have to be matched. Only a member declaration with *no* tuple element names in it can override or implement a member in which tuple element names are found, without matching those names.
|
|
|
|
## Issue 2: VS 2015 Implement Interface will explicitly spit the attributes, but C# 7.0 doesn't allow them
|
|
|
|
This would have been a problem with `dynamic` too, but we never really heard of it. Maybe there was less usage, more of a gap between framework version, or the impact was less because we didn't have round tripping back then. For whatever reason, this never seemed to be a problem before.
|
|
|
|
Options:
|
|
|
|
- Keep the attribute usage an error
|
|
- Allow it but ignore it
|
|
- possibly with a warning when tuple syntax is used
|
|
|
|
|
|
### Conclusion
|
|
Keep it an error, willing to take it up again if necessary. We are unsure that this is really a problem.
|
|
|
|
For the future we want to get better at marking attributes as compiler-only in a well-known way, so that they won't be put in source code even by older compilers. One option is to do that using `Obsolete`; then no compiler will allow them in source, but will be happy to detect or emit them in metadata.
|
|
|
|
|
|
# Deconstruction without ValueTuple
|
|
|
|
Deconstructing assignments are expressions, and their value is a tuple. This is natural from a language perspective, even though the result value of assignments is rarely used, and we expect this goes for deconstructing assignments as well.
|
|
|
|
Unfortunately, even in the common case where the assignment occurs as an expression statement (so the resulting tuple is discarded), the compiler currently still requires the associated `ValueTuple<...>` type to be present. That means you have the hassle of importing `ValueTuple` even when you never use a tuple - if you happen to make use of deconstruction.
|
|
|
|
## Conclusion
|
|
We'll rewrite the compiler to be more lazy about `ValueTuple<...>`, requiring it only when it actually needs it for code gen.
|
|
|
|
|
|
# Non-trailing named arguments
|
|
|
|
It looks like this is making it into C# 7.2. The basic idea is that we allow named arguments *that are in their place, positionally* to be followed by positional arguments. This is mainly for the self-documenting convenience of calling out the meaning of a positional argument where it is non-obvious in the calling context. However, it can be useful for disambiguation of overloads also.
|
|
|