Click here to Skip to main content
15,881,709 members
Articles / Programming Languages / C#
Article

Receive Notification when a Stream Changes

Rate me:
Please Sign up or sign in to vote.
4.53/5 (11 votes)
14 Jun 20044 min read 46K   293   30   5
Streams are a fundamental part of .NET, but lack the ability to notify the client of changes. NotifyStream and VersionStream attempt to solve this notification problem.

Introduction

Streams are a fundamental part of .NET, but lack the ability to notify the client of changes. What do I mean by notify? Notification is the ability for the client to be aware, at all time, of the state and transition of a stream. In other words, no change is made to the stream without the client being aware of that change, regardless of the number of references to that stream and where the change occurred.

This article describes various ways that a stream can be implemented to allow notification to occur.

Notifying Client of Stream Changes

NotifyStream attempts to solve the notification problem. The client can subscribe to events so that it can be notified when changes have occurred. To maintain compatibility with the multitude of classes and components, NotifyStream inherits directly from Stream, allowing it to be used by classes such as StreamReader.

C#
public class NotifyStream : Stream
{
    ...
}

At a very fundamental level, all NotifyStream does is wrap another stream (herein will be referred to as the base stream). So, the base stream needs to be passed to the constructor as shown in the following code:

public class NotifyStream : Stream
{
    m_baseStream = baseStream;
        
    public NotifyStream( Stream baseStream )
    {
        m_baseStream = baseStream;
    }
    
    ...
}

This allows all calls to be forwarded to the base stream. Some operations are required as they are abstract in the Stream class, others are optional, but providing them will allow for a more complete implementation. As you can see in the code below, the operations in NotifyStream are nothing more then pass-through, operating directly on the base stream.

C#
public class NotifyStream : Stream
{
    ...
    
    public override bool CanWrite
    {
        get{return m_baseStream.CanWrite;}
    }

    public override IAsyncResult BeginRead(
        byte[] buffer,
        int offset,
        int count,
        AsyncCallback callback,
        object state)
    {
        return m_baseStream.BeginRead(buffer,
            offset,
            count,
            callback,
            state);
    }

    public override int EndRead(
        IAsyncResult asyncResult)
    {
        return m_baseStream.EndRead(asyncResult);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return m_baseStream.Seek(offset, origin);
    }

    public override long Length
    {
        get{return m_baseStream.Length;}
    }

    public override int Read(
        byte[] buffer,
        int offset,
        int count)
    {
        return m_baseStream.Read(buffer, offset, count);
    }
    
    ...
}

What has been discussed thus far seems quite uninteresting and appears to perform no special operations on the base stream. It is just an added level of indirection. What makes this stream powerful is the ability to send notifications when the stream has changed, regardless of what reference makes the change (as is shown below). As you can see, each write operation raises the StreamChanged event. That way the client can always be notified.

C#
public class NotifyStream : Stream
{
    ...
    
    public override void Write(
        byte[] buffer,
        int offset,
        int count)
    {
        m_baseStream.Write(buffer, offset, count);
        OnStreamChanged( new EventArgs() );
    }

    public override void WriteByte(byte value)
    {
        m_baseStream.WriteByte(value);
        OnStreamChanged( new EventArgs() );
    }

    public event EventHandler StreamChanged;

    protected virtual void OnStreamChanged( EventArgs ea )
    {
        if( StreamChanged != null )
            StreamChanged(this, ea);
    }
}

Monitoring Stream Version

NotifyStream provides an elegant way of informing the client when changes have occurred, but it comes at a cost. The cost being that there is added overhead when events are dispatched. As a lightweight alternative, you could implement VersionStream which, rather than providing event support, implements a counter that is incremented whenever the stream is modified. By checking this counter, you can determine when it has changed, and even how many times it has changed.

As you can see below, VersionStream is similar in design to NotifyStream where the only implementation change is that a version is incremented rather then an event being raised.

C#
public class VersionStream : Stream
{
    private Stream m_baseStream = null;
    private long m_curVersion = 0;

    public VersionStream( Stream baseStream )
    {
        m_baseStream = baseStream;
    }

    ...

    public override void Write(
        byte[] buffer,
        int offset,
        int count)
    {
        m_baseStream.Write(buffer, offset, count);
        m_curVersion++;
    }

    public override void WriteByte(byte value)
    {
        m_baseStream.WriteByte(value);
        m_curVersion++;
    }

    public long Version
    {
        get{return m_curVersion;}
    }
}

Alternative Designs

Both NotifyStream and VersionStream are not meant to be a means to an end, but rather a starting point for more tailored implementations. This section discusses some alternative implementations.

A consequence of the above designs is that they inherit directly from Stream, which means that you are limited in functionality and cannot easily expose specialized stream operations. If you plan to always wrap a specific type of stream, such as a MemoryStream or FileStream, it might benefit you to inherit from that stream directly, allowing you to forward any specialized operations.

You can add any additional operations that you need for your application.

You can add more events for your application to consume. For example, you could implement an event for when data is read from the stream. In addition, you could provide additional information to the event handler such as the content that was added. Right now only the basic EventHandler is used.

Conclusion

Both the NotifyStream and VersionStream have the ability to provide notification to the client when changes have occurred. NotifyStream employs a push mechanism in which when a change is made, events are raised regardless of the current execution path. VersionStream employs a pull mechanism in which it is the responsibility of the client to query for the version and perhaps compare it with some previous version.

There are several benefits that these two classes have. They are useful in libraries where you may not have control over what the client does but need to monitor the state of the stream when changes occur. This might be used when the contents of a stream affect the state of the library. One such internal state may be the need to refresh a cache. There are many classes in .NET such as Image and XmlDocument which are loaded from a stream but hold no persistent references to the stream. In this case, you might want to monitor the stream for changes so that you can invalidate your cache.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to monitor the Original Stream Pin
Sonrisante25-Jul-08 13:21
Sonrisante25-Jul-08 13:21 
GeneralOverriding "Read" methods Pin
mastergaurav20-Nov-04 1:04
mastergaurav20-Nov-04 1:04 
GeneralRe: Overriding "Read" methods Pin
Peter Rilling9-Dec-04 9:29
Peter Rilling9-Dec-04 9:29 
GeneralEventArgs.Empty Pin
Sebastien Lorion22-Jun-04 17:16
Sebastien Lorion22-Jun-04 17:16 
GeneralRe: EventArgs.Empty Pin
Peter Rilling23-Jun-04 9:26
Peter Rilling23-Jun-04 9:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.