Deconstruct IDisposable in using. #2623
Replies: 10 comments
-
Well, writing it manually is only slightly longer...But is it too annoying to warrant a language change you decide. using (var d = GetDisposable())
{
var (x, y) = d;
} Your first proposal hides a bit too much for my taste. |
Beta Was this translation helpful? Give feedback.
-
I'm not exactly sure you see as being hidden using (GetDisposable().Deconstruct(out var x, out var y))
{
} Which actually works, if you change the return value of Deconstruct(this making it NOT work for actual deconstruction). This obviously has an unnamed local assigned in order to dispose of it. I also find the actual error message odd, saying "type used in using statement must be implicitly convertible to IDisposable". What type? Are we constructing a tuple from the Deconstruct call, assigning that tuple to an unnamed local and then deconstructing it into variable? IMHO, the only variables we're declaring in that using statement are x and y. Leaving the only logical type to be "used" as the result of GetDisposable(). There are no(or should not be) any tuples involved in a statement |
Beta Was this translation helpful? Give feedback.
-
I don't see the use-case here.
If you deconstruct an This goes against your proposal, which explicitly breaks apart the Do you have a concrete use-case in mind? |
Beta Was this translation helpful? Give feedback.
-
The lesson here is that deconstructable types should not implement IDisposable. |
Beta Was this translation helpful? Give feedback.
-
I suspect there are probably cases where deconstructable |
Beta Was this translation helpful? Give feedback.
-
I would agree that in most cases ownership could pass to deconstructed parts. However that isn't something that is normally done. In the case where the object is not deconstructed, what do you dispose? The object? A property on the object? My use case is that I have multiple instances of a database, all with the same schema. I also have another database that includes information about those databases. I then have a class that provides access to a database as well as a description. Simplified public class DBInstance: IDisposable
{
public Guid Id { get;}
public string InstanceName { get; }
public IDbConnection Connection { get; }
public void Deconstruct(out string instanceName, out IDbConnection connection);
} So we have using(var (name,connection) = GetInstanceById(<id>))
{
connection.CreateCommand()
....
} or using (var instance = GetInstanceById(<id>))
{
instance.Connection.CreateCommand()
...
} or with deconstruction using (var instance = GetInstanceById(<id>))
{
var (name, connection) = instance;
connection.CreateCommand()
...
} I agree that it's not really necessary syntactically, but it reads well to me. I certainly shouldn't be calling Dispose on instance.Connection. I don't own that, and that shouldn't change just because of deconstruction IMO. |
Beta Was this translation helpful? Give feedback.
-
I'd argue that once you've deconstructed it into the name and connection, you've taken ownership of the connection, and it's your responsibility to dispose the connection. If you were just grabbing some properties and putting them in locals, I'd write simply: var (name, connection) = (instance.Name, instance.Connection); The deconstruction to me is something more: itvs breaking apart the original and storing its component parts in locals. Your proposed syntax makes it look like both the name and the connection should be disposed by the |
Beta Was this translation helpful? Give feedback.
-
I would disagree that deconstructing implies I'm now responsible. That would mean I need to know the implementation of Does that mean var instance = GetInstance();
var (name, connection) = instance; vs. var (name, connection) = GetInstance(); I should call What about var instance = GetInstance();
var (name,_) = instance; In the case of direct deconstruction we all know there was an object in a temporary, so I don't see why it's hiding anything. To me my proposed syntax is clear, I understand it isn't to everyone. That was actually what I typed and was a bit surprised that it didn't compile. I think "passing responsibility for disposal" is dangerous, and something that wouldn't be expected if you were doing variable assignment without the syntactic sugar of deconstruction. |
Beta Was this translation helpful? Give feedback.
-
Then
I'd argue that you should call Dispose on connection in both cases. However as we've found the situation is very ambiguous, so I'd try to avoid mixing deconstruction and
No we don't. As I said earlier, that's another reason why I think this is confusing. Manual resource management is hard, and it's easy to get wrong, so there's IMO a lot to be said for keeping things clear and understandable. |
Beta Was this translation helpful? Give feedback.
-
This is really not the use case for deconstruction. Deconstruction is meant for types that behave like tuples. They are data-only class (without behavior other than maybe equality, etc). They should not own any aspect of disposing of resources. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Similar to, but may be in conflict with:
#1898
#1473
If I have a disposable class:
Deconstructing this object on assignment will cause loss of reference to the IDisposable object itself.
In this case it seems a statement such as:
would hold an unnamed reference to the IDisposable.
Much like:
does not require a variable assignment.
Beta Was this translation helpful? Give feedback.
All reactions