2019-01-18 00:55:44 +01:00
|
|
|
|
|
|
|
# C# Language Design Notes for Jan. 16th, 2019
|
|
|
|
|
|
|
|
## Agenda
|
|
|
|
|
|
|
|
1. Shadowing in lambdas
|
|
|
|
2. pattern-based disposal in `await foreach`
|
|
|
|
|
|
|
|
## Discussion
|
|
|
|
|
|
|
|
### Shadowing in nested functions
|
|
|
|
|
|
|
|
*Q: Allow shadowing for all lambdas as well?*
|
|
|
|
|
|
|
|
**A**: Yes.
|
|
|
|
|
|
|
|
*Q: Allow shadowing with range variables in LINQ queries?*
|
|
|
|
|
|
|
|
```C#
|
|
|
|
// char c;
|
|
|
|
var q = from c in s
|
|
|
|
from c2 in s
|
|
|
|
where c != ' '
|
|
|
|
select c;
|
|
|
|
var q2 = s
|
|
|
|
.SelectMany(c => s, (c, c2) => new { c, c2 })
|
|
|
|
.Where(_x => _x.c != ' ')
|
|
|
|
.Select(_x => _x.c);
|
|
|
|
```
|
|
|
|
|
|
|
|
`c` and `c2` can't be named the same because they are equivalent to two
|
|
|
|
parameters being named the same. However, should the new `c` be able to
|
|
|
|
shadow the local `c`?
|
|
|
|
|
|
|
|
**A**: Let's look at the implementation and decide based on the complexity.
|
|
|
|
|
|
|
|
### Pattern-based disposal in `await foreach`
|
|
|
|
|
|
|
|
`WithCancellation` and `ConfigureAwait` both return a custom type that does
|
|
|
|
not implement the interface, just the pattern. `await foreach` does not pick
|
|
|
|
it up because it does not have pattern-based disposal and the type cannot
|
|
|
|
implement the `IAsyncDisposable` type because it does not produce a `ValueTask`
|
|
|
|
return for disposal.
|
|
|
|
|
|
|
|
Proposals:
|
|
|
|
|
|
|
|
1. Just allow pattern-based disposal for `IAsyncDisposable`. Only allow
|
|
|
|
pattern-based disposable for `IDisposable` in ref structs.
|
|
|
|
|
|
|
|
a. For `await foreach`, use the pattern, then dynamically check for
|
|
|
|
interface and use it if present.
|
|
|
|
|
|
|
|
b. For `await foreach`, use the pattern, then statically check for the
|
|
|
|
interface and use it if present.
|
|
|
|
|
2019-01-28 06:02:06 +01:00
|
|
|
The dynamic check in (1a) is used because in C# 1.0 the non-generic `IEnumerator`
|
2019-01-18 00:55:44 +01:00
|
|
|
did not implement IDisposable.
|
|
|
|
|
|
|
|
The next question is whether to consider extension methods. The standard pattern
|
|
|
|
we've previously settled on is:
|
|
|
|
|
|
|
|
1. Check for pattern
|
|
|
|
2. Check for interface
|
|
|
|
3. Check for extension methods
|
|
|
|
|
|
|
|
Can we use this pattern for `IAsyncDisposable`? The primary question is whether
|
|
|
|
to consider extension methods.
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
2019-01-28 10:27:48 +01:00
|
|
|
Let's do 1b. We do not have a non-generic `IAsyncEnumerator` that doesn't
|
2019-01-18 00:55:44 +01:00
|
|
|
implement `IAsyncDisposable`, so the dynamic check is unnecessary.
|
|
|
|
|
|
|
|
We will not consider extension methods for `IAsyncDisposable` or
|
|
|
|
`IDisposable` (on ref structs). We will consider extension methods for
|
2019-01-28 06:02:06 +01:00
|
|
|
`IAsyncEnumerable` and `IEnumerable`.
|