2018-12-05 19:03:10 +01:00
|
|
|
|
|
|
|
# C# LDM notes for Dec 3, 2018
|
|
|
|
|
|
|
|
## Agenda
|
|
|
|
|
|
|
|
1. `using` declaration open issues
|
|
|
|
2. Return type of `Range` indexer on array and FX types
|
|
|
|
|
|
|
|
## Discussion
|
|
|
|
|
|
|
|
### `using` declaration open issues
|
|
|
|
|
|
|
|
*Q: Do we want to reserve '_' to mean discard in a `using` e.g., `using var _
|
|
|
|
*= ...;`?
|
|
|
|
This would let the user set the scope for an expression without creating an
|
|
|
|
identifier.*
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
Yes, this seems useful.
|
|
|
|
|
|
|
|
#### Deconstruction
|
|
|
|
|
|
|
|
*Q: Do we want to allow a deconstruction in the declaration e.g., `using var
|
|
|
|
(x, y) = GetResources()`?*
|
|
|
|
|
|
|
|
We noted that this could mean two different things: the outer tuple is
|
|
|
|
disposed, then deconstructed, or the elements are deconstructed, then each
|
|
|
|
is disposed.
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
This seems too complicated for the marginal benefit. Rejected.
|
|
|
|
|
|
|
|
#### `goto` and using declarations
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
```C#
|
|
|
|
goto label;
|
|
|
|
using var x = ...;
|
|
|
|
label:
|
|
|
|
...
|
|
|
|
```
|
|
|
|
|
|
|
|
Can you `goto` past a `using` declaration? Can you `goto` before a `using`
|
|
|
|
declaration? Can you `goto` in the same block, but not past the `using`? Can
|
|
|
|
you even have labels and `using var` in the same block?
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
You definitely can't `goto` "past" a using var. That would be similar to
|
|
|
|
jumping "into" a try-finally.
|
|
|
|
|
|
|
|
We could allow jumping "out" of a `using var`, but we're concerned it may
|
|
|
|
be confusing, since there may not be a syntactical indication that the
|
|
|
|
disposal is executed.
|
|
|
|
|
|
|
|
When `goto`ing a label in the same statement list as a `using var`, the
|
|
|
|
`goto` must not have a `using var` syntactically between the `goto` and
|
|
|
|
the label.
|
|
|
|
|
|
|
|
#### Extension `Dispose()` and `Nullable<T>`
|
|
|
|
|
|
|
|
*Q: Should an extension `Dispose()` method be called if the receiver is null?*
|
|
|
|
|
|
|
|
No, the behavior is defined as `item?.Dispose()`, and so `Dispose()` will not
|
|
|
|
be called for `null` values.
|
|
|
|
|
|
|
|
*Q: For a value of type `S?` and extension methods `Dispose(this S? s)` and
|
|
|
|
`Dispose(this S s)`, which is called?*
|
|
|
|
|
|
|
|
Same resolution as above: the behavior is defined as `value?.Dispose()`, so
|
|
|
|
we do the same thing as `item?.Dispose()` (which picks `Dispose(this S s)`).
|
|
|
|
|
|
|
|
|
|
|
|
### Return values of Range indexers
|
|
|
|
|
|
|
|
Proposals:
|
|
|
|
|
|
|
|
1. Add indexers that return the same types (string returns a string, array returns
|
|
|
|
an array, Span returns a Span, etc.)
|
|
|
|
2. Don't add indexers that allocate/copy, add overloads to do slicing with ranges
|
2019-05-25 07:31:46 +02:00
|
|
|
3. Add indexers that return Span\<T>
|
|
|
|
4. Add indexers that return Memory\<T>
|
2018-12-05 19:03:10 +01:00
|
|
|
5. Do nothing
|
|
|
|
|
|
|
|
(3) and (2) are initially attractive because they automatically produce the
|
|
|
|
fastest possible behavior with no allocation. Essentially, users are in a pit
|
|
|
|
of success from a performance perspective.
|
|
|
|
|
|
|
|
There are two main problems with this:
|
|
|
|
|
|
|
|
1. Span is not considered "general purpose" and can be quite difficult to use.
|
|
|
|
Not being able to store the value in a field or convert it to an interface
|
|
|
|
is difficult.
|
|
|
|
2. Span and Memory are very new types and most code doesn't use them. Users
|
|
|
|
would often be forced to coerce into another type anyway. This is particularly
|
|
|
|
bad for strings, where almost all operations are only available on the `string`
|
|
|
|
type.
|
|
|
|
|
|
|
|
The opposing proposal is (1). The theory is that users are more likely to be able
|
|
|
|
use that they pass in. For many users, the additional allocation and copying will
|
|
|
|
not be prohibitive. For performance sensitive users, instead of passing arrays
|
|
|
|
around directly, they can proactively convert to Span or Memory and all subsequent
|
|
|
|
indexing operations will be 0-allocation.
|
|
|
|
|
|
|
|
The main problem with (1) is performance-sensitive users are essentially in a
|
|
|
|
pit of failure in this proposal. The short syntax makes the indexers
|
|
|
|
attractive, but likely to harm performance.
|
|
|
|
|
|
|
|
It was also noted that indexers are generally not supposed to hide expensive
|
|
|
|
or complicated operations, according to .NET coding guidelines. However,
|
|
|
|
Range indexers are fundamentally new operations (they even return a different
|
|
|
|
type -- a collection instead of an element) and it's not clear we should
|
|
|
|
naively apply the same standards for existing indexers to Range indexers.
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
Settled on (1), with overloads for `AsSpan(Range r)` that return a `Span<T>`.
|
|
|
|
This should reduce the work necessary to go directly from a Span-compatible
|
|
|
|
types to Span slices.
|
|
|
|
|
|
|
|
CoreFX Proposal: https://github.com/dotnet/designs/pull/48
|
|
|
|
|
|
|
|
*Q: Do we want to allow Range/Index indexers on multidimensional arrays? They
|
|
|
|
are currently prohibited.*
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
Undecided. We need to discuss this in another LDM.
|