Is a deep copy of the structure type also placed when in the "Using ......" block?

Suppose I have a structure type that implements IDisposible, and if I use the following codes:

using (MyStruct ms = new MyStruct()) { InnerAction(ms); //Notice "InnerAction" is "InnerAction(MyStruct ms)" } 

Of course, I see after using the ms block. However, what about the structure in "InnerAction"? Is he still alive due to a deep copy, or is he also located?

If it is still alive (not installed), Should I use "ref" for "InnerAction"?

Please give me your proof :)

thanks all.

+6
source share
3 answers

This is worse than you think: ms is not even reclaimed.

The reason is that the using statement makes the internal copy that it invokes and removes the 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 are some of the decompiled ILs:

 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 

Note that a compiler is created in IL_000E that is created locally ( CS$3$0001 ), 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 invoked against this local, not ms (which is stored at location 0).

The result is that both ms and the copy that InnerAction is holding InnerAction are both not located.

Conclusion : do not use structures in using statements.

EDIT: as @Weston points out in the comments, you can manually place the structure and act on the boxed instance as it is then on the heap. This way you can get the instance to be deleted, but if you returned it to the structure in the using statement, you only save the copy before the instance is deleted. In addition, boxing removes the benefit of turning off the heap, which you are probably still here.

 MyStruct ms = new MyStruct(); var disposable = (IDisposable)ms; using (disposable) { InnerAction(disposable); } ((MyStruct)disposable).IsDisposed.Dump(); 
+4
source

The behavior of your code depends on the internal implementation of MyStruct.

Consider the following implementation:

 struct MyStruct : IDisposable { private A m_A = new A(); private B m_B = new B(); public void Dispose() { m_A.Dispose(); m_B.Dispose(); } } class A : IDisposable { private bool m_IsDisposed; public void Dispose() { if (m_IsDisposed) throw new ObjectDisposedException(); m_IsDisposed = true; } } class B : IDisposable { private bool m_IsDisposed; public void Dispose() { if (m_IsDisposed) throw new ObjectDisposedException(); m_IsDisposed = true; } } 

In the above code, the implementation of MyStruct delegates the call to Dispose to other types of links. In this case, the instance in your example may be considered "Disposed" after the using block completes. Similar behavior can be achieved by maintaining an internal reference to the logical element, indicating whether the class is located.

However, in the examples from @codekaizen's answer and in the @xanatos comment, the behavior is that only the copy is located, as indicated there.

The bottom line is that you have the ability to force your structure to behave properly with the remote template, but I would avoid this because it is very error prone.

+2
source

It seems to me that it is unfortunate that the C # developers decided that using using with the structure should lead to the fact that all methods in this structure (including Dispose ) will receive copies of them, since this behavior leads to slower code than to the original , eliminates what would otherwise be some useful semantics, and under no circumstances can I determine what would lead to otherwise broken code working correctly. However, behavior is what it is.

Therefore, I would suggest that no structure should implement IDisposable any way that is expected to change the structure itself. The only structure types that implement IDisposable must match one or two of the following patterns:

  • A structure is used to encapsulate an invariable reference to an object, and the structure behaves as if this state of the object is its own. I can’t think of where I saw this template used to encapsulate objects that need to be deleted, but this would seem possible.

  • A structure type implements an interface that IDisposable inherits, and some of its implementations require cleaning. If the structure itself does not need to be cleaned up, and the method of deleting it does nothing, then the fact that the delete method is called on the copy will not make any difference, except that the system will spend time creating a useless copy of the structure before calling do is an impossible method.

Note that the behavior of the C # using statement causes problems not only when using Dispose , but also when calling other methods. Consider:

 void showListContents1(List<string> l) { var en = l.GetEnumerator(); try { while(en.MoveNext()) Console.WriteLine("{0}", en.Current); } finally { en.Dispose(); } } void showListContents(List<string> l) { using(var en = l.GetEnumerator()) { while(en.MoveNext()) Console.WriteLine("{0}", en.Current); } } 

While these two methods look equivalent, the first will work, and the second will not. In the first method, each call to MoveNext will act on the en variable and thus advance the counter. In the second case, each call to MoveNext will act on a different copy of en ; none of them will ever promote the en counter. The fact that the Dispose call is invoked in the second case when copying en will not be a problem, since this copy does nothing. Unfortunately, the way C # handles struct-type using arguments also breaks the code in the using statement.

0
source

All Articles