473,408 Members | 1,873 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,408 software developers and data experts.

Stream and IDisposable in C#2.0, design blunder (well, 100-year sleepis more like it)

In C# 2.0 System.IO.Stream is declared as:

public class Stream: ..., IDisposable {
...
public void Dispose();
public void Dispose(bool);
IDisposable.Dispose();
}

Which must be a design-blunder, if not a 100-year sleep. It prevents
polymorphic disposing of Stream instances through ((Stream)s).Dispose().

Have a look at some ways to *try* to implement a Stream which needs a
custom dispose:

public class MyStream: Stream {
...
/* first, the natural, right and proper way: */
public override void Dispose(); // Dispose is non-virtual
/* bring on the hacks: */
public new Dispose(); // wrong semantics on ((Stream)s).Dispose()
void IDisposable.Dispose(); // using(Stream s = new MyStream...) ?!
}

The bottom-line is that Stream implementations with custom Dispose
*cannot* possibly behave correctly in *all* usage scenarios, and even
worse in the pretty-common scenario where the user knows the instance as
Stream by polymorphism and invokes Dispose yields *wrong* semantics,
*silently* using all possible implementations of MyStream.

To find out what happens in the "using" case, I turned to the ECMA
draft spec.
(http://download.microsoft.com/downlo...9/standard.pdf,
p. 238). It is not really easy to read. To me ECMA sounds like it
declares "public void Dispose()" as "the magic protocol for using", with
"IDisposable.Dispose()" as a backup method. I understand that reasoning
for value-types, in order to actually mutate the value, instead of
disposing a Boxed copy of the value-type.

Writing a test-program which is found at the bottom of the post I
dreaded and predicted the output:

Bar.Dispose()
Bar.Dispose()
Foo.Dispose()
Foo.Dispose()
Baz.Dispose()
Baz.Dispose()

Since the resource-type "R", in this case, Foo and Bar respectively
declares a single public, void-returning, parameterless method named
Dispose.

But the actual output from vs2005 is (as I *hoped* ECMA would have
specified):

Bar.Dispose()
Bar.IDisposable.Dispose()
Foo.Dispose()
Bar.IDisposable.Dispose()
Baz.Dispose()
Baz.IDisposable.Dispose()

Which is almost good enough... only the virtual dispatch in
((Foo)bar).Dispose() is missing -- because of the non-virtual
Foo.Dispose blunder. Note that this explicitly *does* *not* invoke
Baz.Dispose() when using(baz).

I can't see (in my current anger anyway :) how a correct ECMA
implementation could produce the above output.

I see no reason to allow "using" of non-IDisposable reference-types, and
a hacky protocol through "public void Dispose()" for value-types. A
slightly less intrusive protocol would be to execute using without
boxing values as a special-case, limiting the special-cases to a
specific interface instead of contaminating the normal name-space for
methods.

<having a fit>
On top of *that*, Stream has now got a Dispose(bool). what good can that
possibly do? Stream.Dispose() is about *semantics* not implementation in
managed vs. unmanaged space.

What use is it to be able to *possibly* "release managed resources"
polymorphicly in a semantic statement declaring the Stream dead.
</having a fit>

OK, not that's off my chest I better go find the right place to report
this so it can get fixed.

====> test program <====
using System;
using System.IO;

class VirtualDisposeExample
{
public class Foo : IDisposable
{
public void Dispose()
{ Console.WriteLine("Foo.Dispose()"); }
void IDisposable.Dispose()
{ Console.WriteLine("Foo.IDisposable.Dispose()"); }
}
public class Bar : Foo, IDisposable
{
public new void Dispose() { Console.WriteLine("Bar.Dispose()"); }
void IDisposable.Dispose()
{ Console.WriteLine("Bar.IDisposable.Dispose()"); }
}
public struct Baz : IDisposable
{
public void Dispose()
{ Console.WriteLine("Baz.Dispose()"); }
void IDisposable.Dispose()
{ Console.WriteLine("Baz.IDisposable.Dispose()"); }
}
static void Main(string[] args)
{
Bar bar = new Bar();
bar.Dispose();
using (bar)
; // Dispose bar through using
Foo foo = bar;
foo.Dispose();
using (foo)
; // Dispose foo through using
Baz baz = new Baz();
baz.Dispose();
using (baz)
; // Dispose baz through using
}
}
--
Helge
May 15 '06 #1
4 2011
I'm not sure I see the big problem - you just override Disposing, which is
called by the base Dispose (and IIRC finalizer) implementations; the
following works fine; all 4 usages dispose correctly...

Am I missing something in your post?

using System;
using System.IO;

namespace WindowsApplication2 {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
using (MyStream stream1 = new MyStream()) {

}
MyStream stream2 = new MyStream();
stream2.Dispose();
Stream stream3 = new MyStream();
stream3.Dispose();
IDisposable stream4 = new MyStream();
stream4.Dispose();
Console.ReadLine();
}
}

public class MyStream : Stream {
protected override void Dispose(bool disposing) {
System.Diagnostics.Debug.WriteLine("Disposing");
base.Dispose(disposing);
}

// *** the rest is just junk to get it to compile

public override int Read(byte[] buffer, int offset, int count) {
throw new Exception("The method or operation is not
implemented.");
}
public override void Write(byte[] buffer, int offset, int count) {
throw new Exception("The method or operation is not
implemented.");
}
public override bool CanRead {
get { throw new Exception("The method or operation is not
implemented."); }
}
public override bool CanWrite {
get { throw new Exception("The method or operation is not
implemented."); }
}
public override bool CanSeek {
get { throw new Exception("The method or operation is not
implemented."); }
}
public override void Flush() {
throw new Exception("The method or operation is not
implemented.");
}
public override long Seek(long offset, SeekOrigin origin) {
throw new Exception("The method or operation is not
implemented.");
}
public override void SetLength(long value) {
throw new Exception("The method or operation is not
implemented.");
}
public override long Length {
get { throw new Exception("The method or operation is not
implemented."); }
}
public override long Position {
get {
throw new Exception("The method or operation is not
implemented.");
}
set {
throw new Exception("The method or operation is not
implemented.");
}
}
}

}
May 15 '06 #2
Helge,

Forgive me for saying so, but you haven't actually read the
documentation on the suggested implementation of IDisposable, have you?

If you had, you would have seen that the only difference between the
public Dispose method, and the Dispose method which you are allowed to
override is that the Dispose method calls Close, which then calls the
overridable Dispose method, which then indicates to the CLR that the
instance should not be finalized.

You should be able to do all that you want in the Dispose method that
you are able to override.

Even if you wanted to have the finalizer run after you call Dispose (for
what reason, I don't know, as I think you have issues with the lifetime
management aspect of your design if you do), you can always override Close,
which is marked as virtual.

I don't see how you haven't been left an option in this case.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"Helge Jensen" <he**********@slog.dk> wrote in message
news:44**************@slog.dk...
In C# 2.0 System.IO.Stream is declared as:

public class Stream: ..., IDisposable {
...
public void Dispose();
public void Dispose(bool);
IDisposable.Dispose();
}

Which must be a design-blunder, if not a 100-year sleep. It prevents
polymorphic disposing of Stream instances through ((Stream)s).Dispose().

Have a look at some ways to *try* to implement a Stream which needs a
custom dispose:

public class MyStream: Stream {
...
/* first, the natural, right and proper way: */
public override void Dispose(); // Dispose is non-virtual
/* bring on the hacks: */
public new Dispose(); // wrong semantics on ((Stream)s).Dispose()
void IDisposable.Dispose(); // using(Stream s = new MyStream...) ?!
}

The bottom-line is that Stream implementations with custom Dispose
*cannot* possibly behave correctly in *all* usage scenarios, and even
worse in the pretty-common scenario where the user knows the instance as
Stream by polymorphism and invokes Dispose yields *wrong* semantics,
*silently* using all possible implementations of MyStream.

To find out what happens in the "using" case, I turned to the ECMA draft
spec.
(http://download.microsoft.com/downlo...9/standard.pdf,
p. 238). It is not really easy to read. To me ECMA sounds like it declares
"public void Dispose()" as "the magic protocol for using", with
"IDisposable.Dispose()" as a backup method. I understand that reasoning
for value-types, in order to actually mutate the value, instead of
disposing a Boxed copy of the value-type.

Writing a test-program which is found at the bottom of the post I dreaded
and predicted the output:

Bar.Dispose()
Bar.Dispose()
Foo.Dispose()
Foo.Dispose()
Baz.Dispose()
Baz.Dispose()

Since the resource-type "R", in this case, Foo and Bar respectively
declares a single public, void-returning, parameterless method named
Dispose.

But the actual output from vs2005 is (as I *hoped* ECMA would have
specified):

Bar.Dispose()
Bar.IDisposable.Dispose()
Foo.Dispose()
Bar.IDisposable.Dispose()
Baz.Dispose()
Baz.IDisposable.Dispose()

Which is almost good enough... only the virtual dispatch in
((Foo)bar).Dispose() is missing -- because of the non-virtual Foo.Dispose
blunder. Note that this explicitly *does* *not* invoke Baz.Dispose() when
using(baz).

I can't see (in my current anger anyway :) how a correct ECMA
implementation could produce the above output.

I see no reason to allow "using" of non-IDisposable reference-types, and a
hacky protocol through "public void Dispose()" for value-types. A slightly
less intrusive protocol would be to execute using without boxing values as
a special-case, limiting the special-cases to a specific interface instead
of contaminating the normal name-space for methods.

<having a fit>
On top of *that*, Stream has now got a Dispose(bool). what good can that
possibly do? Stream.Dispose() is about *semantics* not implementation in
managed vs. unmanaged space.

What use is it to be able to *possibly* "release managed resources"
polymorphicly in a semantic statement declaring the Stream dead.
</having a fit>

OK, not that's off my chest I better go find the right place to report
this so it can get fixed.

====> test program <====
using System;
using System.IO;

class VirtualDisposeExample
{
public class Foo : IDisposable
{
public void Dispose()
{ Console.WriteLine("Foo.Dispose()"); }
void IDisposable.Dispose()
{ Console.WriteLine("Foo.IDisposable.Dispose()"); }
}
public class Bar : Foo, IDisposable
{
public new void Dispose() { Console.WriteLine("Bar.Dispose()"); }
void IDisposable.Dispose()
{ Console.WriteLine("Bar.IDisposable.Dispose()"); }
}
public struct Baz : IDisposable
{
public void Dispose()
{ Console.WriteLine("Baz.Dispose()"); }
void IDisposable.Dispose()
{ Console.WriteLine("Baz.IDisposable.Dispose()"); }
}
static void Main(string[] args)
{
Bar bar = new Bar();
bar.Dispose();
using (bar)
; // Dispose bar through using
Foo foo = bar;
foo.Dispose();
using (foo)
; // Dispose foo through using
Baz baz = new Baz();
baz.Dispose();
using (baz)
; // Dispose baz through using
}
}
--
Helge

May 15 '06 #3
Helge Jensen <he**********@slog.dk> wrote:
In C# 2.0 System.IO.Stream is declared as:

public class Stream: ..., IDisposable {
...
public void Dispose();
public void Dispose(bool);
IDisposable.Dispose();
}

Which must be a design-blunder, if not a 100-year sleep. It prevents
polymorphic disposing of Stream instances through ((Stream)s).Dispose().
You can polymorphically close a stream with ((IDisposable)s).Dispose()
or ((Stream)s).Close(). I don't understand your problem.
Have a look at some ways to *try* to implement a Stream which needs a
custom dispose:


You're supposed to override 'void Dispose(bool)'. The documentation is
quite clear on the pattern for this.

-- Barry
May 15 '06 #4


Helge Jensen wrote:
In C# 2.0 System.IO.Stream is declared as:


Thanks to everyone for the pointers to the documentation. I have read it
and realize that I can achieve what I need by overriding Dispose(bool
disposing). I rest my case on that point.

May 15 '06 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: xeys_00 | last post by:
Well, I'm auditing CS164 again. C++ part 1, basically. I'd like to know if I can submit some code to the group and get some opinions, as the instructor is not going to do anything more than let me...
6
by: Yechezkal Gutfreund | last post by:
I have been using the following code (successfully) to read Xml formated text packets from a TCP stream. The output from the server stream consists of a sequence of well formed Xml documents...
4
by: Arjen | last post by:
Hi, Here I have made some sample code. I want to know if I'm using IDisposable on the right way. public class Person { public string name; public string address; }
2
by: Prashant | last post by:
Peculiar problem, All of a sudden today in one of our forms a few of the controls have disappered. The code behind the scenes still exists. No errors , nothing. The controls just don't show for...
3
by: Don | last post by:
Response.ContentType() = "application/vnd.ms-excel" Response.Charset = "" Response.BinaryWrite(stream) The code when executed causes the client to open an excel document and display the...
12
by: Cordell Lawrence \(News Group\) | last post by:
There an ongoing discussion between a colleague and myself about the usefulness of the IDisposable pattern beyond the reclamation of unmanaged resources. The discussion is somewhat lengthy so I...
13
by: Carl Johansson | last post by:
Being quite new to C#, I may have misunderstood this. If so please bear with me! As far as I can understand, any instances of a class that implements the IDisposable interface must call the...
19
by: rbrowning1958 | last post by:
Hello, I am confused by dispose etc. and hope someone can set me right. 1. The Dispose(Bool) the IDE generates for a form has nothing to do with IDisposable, right? 2. So when is this called?...
3
by: Paul | last post by:
Hi all, I currenty have a datalayer and have decided to impliment IDisposable with it. The data layer contains a number of objects which I can call dispose on, but i'm not too sure if you need...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.