Suppose I have a struct type implementing IDisposible, and if I use the codes below:
using (MyStruct ms = new MyStruct())
{
InnerAction(ms); //Notice "InnerAction" is "InnerAction(MyStruct ms)"
}
Of course I see after the block of using, the ms is disposed. However what about the struct in "InnerAction"? Is it still alive because of deep copy or it is also disposed?
If it's still alive (not disposed), Must I use "ref" for "InnerAction"?
Please give me your proof:)
Thx all.
The source of deep and shallow copy of structure in C language If a structure contains pointer variables, dynamic memory is allocated during the use of the structure, and mutual assignments are made between variables of the same type of structure, shallow copy and deep copy problems will be caused. 2. Shallow copy problem
It means that any changes made to a copy of object do not reflect in the original object. In python, this is implemented using “ deepcopy () ” function. In the above example, the change made in the list did not effect in other lists, indicating the list is deep copied.
This implies that, if perform you deep copy on an object that contains reference (object), both the original and copied object refers to different objects and, if you do any changes to the data the copied object they are not reflected in the original object.
In Deep copy, an object is created by copying data of all variables and it also allocates similar memory resources with the same value to the object. In order to perform Deep copy, we need to explicitly define the copy constructor and assign dynamic memory as well if required.
It's worse than you think: ms
is not even disposed.
The reason is that the using
statement makes an internal copy which it calls dispose on in a try/finally construct.
Consider this LinqPad example:
void Main()
{
MyStruct ms;
using (ms = new MyStruct())
{
InnerAction(ms);
}
ms.IsDisposed.Dump();
_naughtyCachedStruct.IsDisposed.Dump();
}
MyStruct _naughtyCachedStruct;
void InnerAction(MyStruct s)
{
_naughtyCachedStruct = s;
}
struct MyStruct : IDisposable
{
public Boolean IsDisposed { get; set; }
public void Dispose()
{
IsDisposed = true;
}
}
Here's some of the decompiled IL:
IL_0000: nop
IL_0001: ldloca.s 01 // CS$0$0000
IL_0003: initobj UserQuery.MyStruct
IL_0009: ldloc.1 // CS$0$0000
IL_000A: dup
IL_000B: stloc.0 // ms
IL_000C: dup
IL_000D: stloc.0 // ms
IL_000E: stloc.2 // CS$3$0001
IL_000F: nop
IL_0010: ldarg.0
IL_0011: ldloc.0 // ms
Notice that in IL_000E a compiler generated local (CS$3$0001
) is created and a copy of ms
is stored there. Later...
IL_001B: ldloca.s 02 // CS$3$0001
IL_001D: constrained. UserQuery.MyStruct
IL_0023: callvirt System.IDisposable.Dispose
IL_0028: nop
IL_0029: endfinally
Dispose
is called against this local, not ms
(which is stored in location 0).
The result is that both ms
and the copy that InnerAction
holds onto are both not disposed.
Conclusion: don't use structs in using
statements.
EDIT: as @Weston points out in the comments, you can manually box the struct and act on the boxed instance, since it then lives on the heap. This way you can get the instance to dispose, but if you had cast it back to the struct in the using
statement, you'll only end up storing a copy before the instance was disposed. Further, boxing removes the benefit of staying off the heap, which you are presumably up to here.
MyStruct ms = new MyStruct();
var disposable = (IDisposable)ms;
using (disposable)
{
InnerAction(disposable);
}
((MyStruct)disposable).IsDisposed.Dump();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With