-
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
Conversation
So please this! 😄 |
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.
@jaredpar I don't know why I started doing this. Is this actually helpful, or would it be nice if I stopped? 😄
proposals/using.md
Outdated
## 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 comment
The reason will be displayed to describe this comment to others. Learn more.
typos resouce
managemente
proposals/using.md
Outdated
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 comment
The reason will be displayed to describe this comment to others. Learn more.
a developers
proposals/using.md
Outdated
|
||
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 | ||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
tyse
proposals/using.md
Outdated
} | ||
``` | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
are
→ or
proposals/using.md
Outdated
``` | ||
|
||
There are no restrictions around `goto`, are any other control flow construct in the face of | ||
`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 comment
The reason will be displayed to describe this comment to others. Learn more.
a `using` declaration
``` | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Now this is cool.
proposals/using.md
Outdated
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Typo prefered
twice, opposite
of approach
proposals/using.md
Outdated
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
geneartion
proposals/using.md
Outdated
|
||
### 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 comment
The reason will be displayed to describe this comment to others. Learn more.
it's
→ its
Clearly based on your feedback, and the feedback of all my peers, I can use all the spelling help I can get. Especially when I'm jet lagged apparently. 😄 |
Sorry if my question is dumb but is the purpose of auto-recognition of Disposable pattern is to avoid implicit cast to IDisposable where possible? Because if you have already implemented Disposable method what is the difficulty to implement IDisposable. |
You can't implement an interface on a |
@OlegKarasik Also: extension methods can make types disposable which you don't own. |
@benaadams , @jnm2 Thanks for the clarification. |
@OlegKarasik And some others, too: #1467 |
proposals/using.md
Outdated
|
||
## Summary | ||
|
||
The language will add two need capabilities around the `using` statement in order to make resource |
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.
Typo: need -> needed
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.
Why couple these two unrelated proposals together?
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.
This is about improving the usability of the using
statement hence grouping all aspects of that together.
Would love to have a using that acts as a defer, to avoid scenarios where I need to either use an IDisposable, or add a "Dispose" method to the object being used. It could be an optional block after the expression:
|
Is there an issue for this where we can discuss? |
Thanks. Moved my comment there |
proposals/using.md
Outdated
@@ -0,0 +1,164 @@ | |||
# using patterns and locals |
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).
@jaredpar Is this ready to merge? |
@jcouv not yet. I need to update for the LDM meeting feedback. |
I hope it's a good place to discuss Here's the example I found: {
using var f1 = new FileStream("...");
using var f2 = new FileStream("..."), f3 = new FileStream("...");
...
// Dispose f3
// Dispose f2
// Dispose f1
} What I understood is that the compiler will try to call My question would be: why we need the {
var f1 = new FileStream("...");
var f2 = new FileStream("..."), f3 = new FileStream("...");
...
// Dispose f3
// Dispose f2
// Dispose f1
} |
@chojrak11 This sounds like RAII, C++ and automatic storage :) The problem I see here is how it should work in that situation: void A(FileStream fs)
{
if (isReadyToDispose)
fs.Dispose()
else
{
Task.Delay(1000).ContinueWith(t => { A(fs); });
}
}
void B()
{
var fs = new FileStream("...");
A(fs);
} Should the I am not sure how this could be handled without usage of explicit |
Yes, it does! Or at least 'deterministic destruction.' How would an explicit |
@chojrak11 The explicit Extending the previous example we could have the following: void B()
{
var fs = new FileStream("...");
A(fs);
using fs1 = new FileStream("...");
// Dispose fs1
} With explicit |
@chojrak11 What you are suggesting would change the meaning of existing programs in a breaking way. |
@HaloFour just realized that. And if the |
@chojrak11 How would that help? If you had a class with |
By not defining it, I think. It's like asking 'how not to call the destructor in C++ when an object goes out of scope?' Maybe we're just realizing that C# needs automatic deterministic destruction, and this issue we're discussing is a good start to have that? Maybe The |
@chojrak11
That's not what this proposal is attempting to achieve. Disposal is intentionally opt-in because there are plenty of scenarios where the current method would not want to dispose of the resource. Ownership is not a concept that is defined in C#. |
@chojrak11
What you do in C++ is that you allocate it dynamically ("on the heap"), possibly using some smart pointer type so that you still get deterministic destruction, but one that's not tied to a single block scope. But there's no such option in C#. |
Makes sense. It was worth trying, though. |
I agree with with @chojrak11. We have a lot of occasions where we definitely know that the object/memory can go away. I personally find the C++ CLI approach a little more initutive where there is a finalizer and a destructor. Nevertheless, this requirement doesn't fit in this discussion, just as a side note. |
The "destructor" is just syntax candy for implementing What C++/CLI offers that is interesting is stack-like semantics for working with reference types. The instances are still allocated on the managed heap and garbage collected, but you get copy semantics (requiring a copy constructor and assignment operator) and RAII (through |
Yes, sure. Nevertheless IDiposable is just an interface that supports a some unified way to do some sort of cleanup. The only problem is that there is no control if the object is GC'ed. Although I dislike the whole stuff that was introduced in C++ CLI it seems to me a lot is currently moving from the C++ syntax to C# which I think is not too bad. Maybe also the internals are also a bit handled differently, but in the end the result matters. So something like a deterministic cleanup would really help a lot in scenarios that require more memory than the system supports even with virtual memory. Our systems for example need more memory than is available, but there is not currently a good control to do your own memory manager in C# when the requirements are like ours. The only way to have a control is to fall back into unmanaged mode. But I think that shouldn't be the approach to handle such things. In the end the discussion is a bit wider, e.g. setnewhandler in C++, WeakReferences, etc. But I think it doesn't fit into this discussion. |
@msedi remember that destruction/disposal is not only about memory deallocation. Memory is my least concern. I'm more concerned with database connections, files, network resources and so forth (this is what IDisposable was invented to help with). I like the idea to open a file and have it automatically closed at the end of the scope. Having a single void MyFunc(void) {
using var myFile = File.Open(@"\path\to\file");
// ... use the file
// now it's automatically closed
} while not having the File MyOpenFile(string path) {
var myFile = File.Open(path); // no 'using' marker
return myFile;
// no automatic cleanup
}
void MyFunc(void) {
using var myFile = MyOpenFile(@"\path\to\file");
// ... use the file
// now it's automatically closed
} In this proposal I only miss Golang's void MyFunc(void) {
using var myFile = File.Open(@"\path\to\file");
defer () => Console.WriteLine("Finished processing the file.");
defer () => Console.WriteLine("Another delegate.");
// ... use the file
// "Another delegate."
// "Finished processing the file."
// now the file is automatically closed
} |
@chojrak11: I absolutely agree. I had the same problem with files. The question is if you really need language support for this or are you able to create your own constructs around this problem? For almost any of my classes that needed something you request here, I have created my own Disposable struct that take the object as a reference and remove/close/free the object when the end of the using block is hit. In that sense I'm completely free to do whatever I like. |
As this proposal replaces end of The |
Proposal to add
using
local variable declarations and theusing
pattern to the language.