-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using patterns and declarations #1703
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
# using patterns and locals | ||
|
||
## Summary | ||
|
||
The language will add two need capabilities around the `using` statement in order to make resoure | ||
managemente simpler: recognize a `using` pattern in addition to `IDisposable` and add a `using` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typos |
||
declaration to the language. | ||
|
||
## Motivation | ||
|
||
The `using` statement is an effective tool for resource management today but it requires quite a | ||
bit of ceremony. Methods that have a number of resources to manage can get syntactically bogged | ||
down with a series of `using` statements. This syntax burden is enough that most coding style | ||
guidelines explicitly have an exception around braces for this scenario. | ||
|
||
The `using` declaration removes much of the ceremony here and gets C# on par with other languages | ||
that include resource management blocks. Additionally the `using` pattern lets a developers expand | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
the set of tyse that can participate here. In many cases removing the need to create wrapper types | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
that only exist to allow for a values use in a `using` statement. | ||
|
||
Together these features allow developers to simplify and expand the scenarios where `using` can | ||
be applied. | ||
|
||
## Detailed Design | ||
|
||
### using declaration | ||
|
||
The language will allow for `using` to be added to a local variable declaration. Such a declaration | ||
will have the same effect as declarating the variable in a `using` statement at the same location. | ||
|
||
``` csharp | ||
if (...) { | ||
using FileStream f = new FileStream(@"C:\users\jaredpar\using.md"); | ||
// statements | ||
} | ||
|
||
// Equivalent to | ||
if (...) { | ||
using (FileStream f = new FileStream(@"C:\users\jaredpar\using.md")) { | ||
// statements | ||
} | ||
} | ||
``` | ||
|
||
The lifetime of a `using` local will extend to the end of the scope in which it is declared. The | ||
`using` locals will then be disposed in the reverse order in which they are declared. | ||
|
||
``` csharp | ||
{ | ||
using var f1 = new FileStream("..."); | ||
using var f2 = new FileStream("..."), f3 = new FileStream("..."); | ||
... | ||
// Dispose f3 | ||
// Dispose f2 | ||
// Dispose f1 | ||
} | ||
``` | ||
|
||
There are no restrictions around `goto`, are any other control flow construct in the face of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
`using` declaration. Instead the code acts just as it would for the equivalent `using` statement: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
``` csharp | ||
{ | ||
using var f1 = new FileStream("..."); | ||
target: | ||
using var f2 = new FileStream("..."); | ||
if (someCondition) { | ||
// Causes f2 to be disposed but has no effect on f1 | ||
goto target; | ||
} | ||
} | ||
``` | ||
|
||
A local declared in a `using` local declaration will be implicitly read-only. This matches the | ||
behavior of locals declared in a `using` statement. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now this is cool. |
||
|
||
The language grammar for `using` declarations will be the following: | ||
|
||
``` | ||
local-using-declaration: | ||
using type using-declarators | ||
|
||
using-declarators: | ||
using-declarator | ||
using-declarators , using-declarator | ||
|
||
using-declarator: | ||
identifier = expression | ||
``` | ||
|
||
Restrictions around `using` declaration: | ||
|
||
- May not appear directly inside a `case` label but instead must be within a block inside the | ||
`case` label. | ||
- May not appear as part of an `out` variable declaration. | ||
- Must have an initializer for each declarator. | ||
- The local type must be implicitly convertible to `IDisposable` or fulfill the `using` pattern. | ||
|
||
### using pattern | ||
|
||
The language will add the notion of a disposable pattern: that is a type which has an accessible | ||
Dispose instance method. Types which fit the disposable pattern can participate in a `using` | ||
statement or declaration without being required to implement `IDisposable`. | ||
|
||
``` csharp | ||
class Resource { | ||
public void Dispose() { ... } | ||
} | ||
|
||
using (var r = new Resource()) { | ||
// statements | ||
} | ||
``` | ||
|
||
This will allow developers to leverage `using` in a number of new scenarios: | ||
|
||
- `ref struct`: These types can't implement interfaces today and hence can't participate in `using` | ||
statements. | ||
- Extension methods will allow developers to augment types in other assemblies to participate | ||
in `using` statements. | ||
|
||
In the situation where a type can be implicitly converted to `IDisposable` and also fits the | ||
`using` pattern, then `IDisposable` will be prefered. While this takes the opposite of approach | ||
of `foreach` (pattern prefered over interface) it is necessary for backwards compatibility. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo |
||
|
||
The same restrictions from a traditional `using` statement apply here as well: local variables | ||
declared in the `using` are read-only, a `null` value will not cause an exception to be thrown, | ||
etc ... The code geneartion will be different only in that there will not be a cast to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
`IDisposable` before calling Dispose: | ||
|
||
``` csharp | ||
{ | ||
Resource r = new Resource(); | ||
try { | ||
// statements | ||
} | ||
finally { | ||
if (resource != null) resource.Dispose(); | ||
} | ||
} | ||
``` | ||
|
||
In order to fit the `using` pattern the Dispose method must be accessible, parameterless and have | ||
a `void` return type. There are no other restrictions. This explicitly means that extension methods | ||
can be used here. | ||
|
||
## Considerations | ||
|
||
### case labels without blocks | ||
|
||
A `using declaration` is illegal directly inside a `case` label due to complications around it's | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
actual lifetime. One potential solution is to simply give it the same lifetime as an `out var` | ||
in the same location. It was deemed the extra complexity to the feature implementation and the | ||
ease of the work around (just add a block to the `case` label) didn't justify taking this route. | ||
|
||
## Future Expansions | ||
|
||
### fixed locals | ||
|
||
A `fixed` statement has all of the properties of `using` statements that motivated the ability | ||
to have `using` locals. Consideration should be given to extending this feature to `fixed` locals | ||
as well. The lifetime and ordering rules should apply equally well for `using` and `fixed` here. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "using patterns" is confusing terminology. I think it should be "pattern-based using" instead (just like we do "pattern-base foreach", etc).