Events
17 Mar, 9 pm - 21 Mar, 10 am
Join the meetup series to build scalable AI solutions based on real-world use cases with fellow developers and experts.
Register nowThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
This article is a feature specification. The specification serves as the design document for the feature. It includes proposed specification changes, along with information needed during the design and development of the feature. These articles are published until the proposed spec changes are finalized and incorporated in the current ECMA specification.
There may be some discrepancies between the feature specification and the completed implementation. Those differences are captured in the pertinent language design meeting (LDM) notes.
You can learn more about the process for adopting feature speclets into the C# language standard in the article on the specifications.
Champion issue: https://github.com/dotnet/csharplang/issues/114
The language will add two new capabilities around the using
statement in order to make resource
management simpler: using
should recognize a disposable pattern in addition to IDisposable
and add a using
declaration to the language.
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 pattern-based using
lets developers expand
the set of types that can participate here. In many cases removing the need to create wrapper types
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.
The language will allow for using
to be added to a local variable declaration. Such a declaration
will have the same effect as declaring the variable in a using
statement at the same location.
if (...)
{
using FileStream f = new FileStream(@"C:\source\using.md");
// statements
}
// Equivalent to
if (...)
{
using (FileStream f = new FileStream(@"C:\source\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.
{
using var f1 = new FileStream("...");
using var f2 = new FileStream("...");
using var f3 = new FileStream("...");
...
// Dispose f3
// Dispose f2
// Dispose f1
}
There are no restrictions around goto
, or any other control flow construct in the face of
a using
declaration. Instead the code acts just as it would for the equivalent using
statement:
{
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.
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:
case
label but instead must be within a block inside the
case
label.out
variable declaration.IDisposable
or fulfill the using
pattern.The language will add the notion of a disposable pattern for ref struct
types: that is a ref struct
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
.
ref struct Resource
{
public void Dispose() { ... }
}
using (var r = new Resource())
{
// statements
}
This will allow developers to leverage using
for ref struct
types. These types can't implement interfaces in C# 8 and hence can't participate in using
statements.
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 generation will be different only in that there will not be a cast to
IDisposable
before calling Dispose:
{
Resource r = new Resource();
try {
// statements
}
finally {
if (r != null) r.Dispose();
}
}
In order to fit the disposable pattern the Dispose
method must be an accessible instance member, parameterless and have
a void
return type. It cannot be an extension method.
Neither of these considerations were implemented in C# 8
A using declaration
is illegal directly inside a case
label due to complications around its
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.
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.
C# feature specifications feedback
C# feature specifications is an open source project. Select a link to provide feedback:
Events
17 Mar, 9 pm - 21 Mar, 10 am
Join the meetup series to build scalable AI solutions based on real-world use cases with fellow developers and experts.
Register now