C#idiomatic C#
how-to-implement-an-observerstandard/events

Core library

How to implement an observer

Implement an observer in .NET. The observer design pattern requires a division between an observer, which registers for notifications, and a provider.

  • page standard/events/how-to-implement-an-observer
  • updated 06/01/2026

The observer design pattern requires a division between an observer, which registers for notifications, and a provider, which monitors data and sends notifications to one or more observers. This article discusses how to create an observer. A related article, How to implement a provider, discusses how to create a provider.

Create an observer

To create an observer, implement the System.IObserver interface. The following steps describe each member you need to define.

  1. Define the observer type that implements the System.IObserver interface.

    The following code defines a type named TemperatureReporter that is a constructed System.IObserver implementation with a generic type argument of Temperature.

sample · csharpdocs/standard/events/snippets/shared/how-to-provider-observer/csharp/observer.csClassDeclaration

csharp
// <All>
// <Subscribe>
// <ClassDeclaration>
namespace TemperatureSample;

public sealed class TemperatureReporter : IObserver<Temperature>
// </ClassDeclaration>
{
    private IDisposable? _unsubscriber;
    private Temperature? _last;

    public void Subscribe(IObservable<Temperature> provider)
    {
        ArgumentNullException.ThrowIfNull(provider);
        _unsubscriber = provider.Subscribe(this);
    }
    // </Subscribe>

    // <Unsubscribe>
    public void Unsubscribe() => _unsubscriber?.Dispose();
    // </Unsubscribe>

    // <ObserverMethods>
    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");

    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }

    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");

        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }

        _last = value;
    }
    // </ObserverMethods>
}
// </All>

sample · vbdocs/standard/events/snippets/shared/how-to-provider-observer/vb/observer.vbClassDeclaration

vb
' <All>
' <Subscribe>
' <ClassDeclaration>
Namespace Global.TemperatureSample

    Public NotInheritable Class TemperatureReporter
        Implements IObserver(Of Temperature)
        ' </ClassDeclaration>

        Private _unsubscriber As IDisposable
        Private _last As Temperature?

        Public Sub Subscribe(provider As IObservable(Of Temperature))
            ArgumentNullException.ThrowIfNull(provider)
            _unsubscriber = provider.Subscribe(Me)
        End Sub
        ' </Subscribe>

        ' <Unsubscribe>
        Public Sub Unsubscribe()
            _unsubscriber?.Dispose()
        End Sub
        ' </Unsubscribe>

        ' <ObserverMethods>
        Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
            Console.WriteLine("Additional temperature data won't be transmitted.")
        End Sub

        ' OnError is informational; observers shouldn't treat it as an exception to handle.
        Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
        End Sub

        Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
            Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")

            If _last.HasValue Then
                Dim previous = _last.Value
                Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
                Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
            End If

            _last = value
        End Sub
        ' </ObserverMethods>
    End Class

End Namespace
' </All>

  1. If the observer needs to unsubscribe before the provider calls System.IObserver.OnCompleted, define a private variable to hold the System.IDisposable returned by System.IObservable.Subscribe, and define a subscription method.

    The private variable unsubscriber stores the System.IDisposable object. The Subscribe method calls the provider's System.IObservable.Subscribe method and assigns the returned object to unsubscriber.

sample · csharpdocs/standard/events/snippets/shared/how-to-provider-observer/csharp/observer.csSubscribe

csharp
// <All>
// <Subscribe>
// <ClassDeclaration>
namespace TemperatureSample;

public sealed class TemperatureReporter : IObserver<Temperature>
// </ClassDeclaration>
{
    private IDisposable? _unsubscriber;
    private Temperature? _last;

    public void Subscribe(IObservable<Temperature> provider)
    {
        ArgumentNullException.ThrowIfNull(provider);
        _unsubscriber = provider.Subscribe(this);
    }
    // </Subscribe>

    // <Unsubscribe>
    public void Unsubscribe() => _unsubscriber?.Dispose();
    // </Unsubscribe>

    // <ObserverMethods>
    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");

    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }

    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");

        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }

        _last = value;
    }
    // </ObserverMethods>
}
// </All>

sample · vbdocs/standard/events/snippets/shared/how-to-provider-observer/vb/observer.vbSubscribe

vb
' <All>
' <Subscribe>
' <ClassDeclaration>
Namespace Global.TemperatureSample

    Public NotInheritable Class TemperatureReporter
        Implements IObserver(Of Temperature)
        ' </ClassDeclaration>

        Private _unsubscriber As IDisposable
        Private _last As Temperature?

        Public Sub Subscribe(provider As IObservable(Of Temperature))
            ArgumentNullException.ThrowIfNull(provider)
            _unsubscriber = provider.Subscribe(Me)
        End Sub
        ' </Subscribe>

        ' <Unsubscribe>
        Public Sub Unsubscribe()
            _unsubscriber?.Dispose()
        End Sub
        ' </Unsubscribe>

        ' <ObserverMethods>
        Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
            Console.WriteLine("Additional temperature data won't be transmitted.")
        End Sub

        ' OnError is informational; observers shouldn't treat it as an exception to handle.
        Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
        End Sub

        Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
            Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")

            If _last.HasValue Then
                Dim previous = _last.Value
                Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
                Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
            End If

            _last = value
        End Sub
        ' </ObserverMethods>
    End Class

End Namespace
' </All>

  1. Define an Unsubscribe method that lets the observer stop receiving notifications before the provider calls System.IObserver.OnCompleted.

sample · csharpdocs/standard/events/snippets/shared/how-to-provider-observer/csharp/observer.csUnsubscribe

csharp
// <All>
// <Subscribe>
// <ClassDeclaration>
namespace TemperatureSample;

public sealed class TemperatureReporter : IObserver<Temperature>
// </ClassDeclaration>
{
    private IDisposable? _unsubscriber;
    private Temperature? _last;

    public void Subscribe(IObservable<Temperature> provider)
    {
        ArgumentNullException.ThrowIfNull(provider);
        _unsubscriber = provider.Subscribe(this);
    }
    // </Subscribe>

    // <Unsubscribe>
    public void Unsubscribe() => _unsubscriber?.Dispose();
    // </Unsubscribe>

    // <ObserverMethods>
    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");

    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }

    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");

        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }

        _last = value;
    }
    // </ObserverMethods>
}
// </All>

sample · vbdocs/standard/events/snippets/shared/how-to-provider-observer/vb/observer.vbUnsubscribe

vb
' <All>
' <Subscribe>
' <ClassDeclaration>
Namespace Global.TemperatureSample

    Public NotInheritable Class TemperatureReporter
        Implements IObserver(Of Temperature)
        ' </ClassDeclaration>

        Private _unsubscriber As IDisposable
        Private _last As Temperature?

        Public Sub Subscribe(provider As IObservable(Of Temperature))
            ArgumentNullException.ThrowIfNull(provider)
            _unsubscriber = provider.Subscribe(Me)
        End Sub
        ' </Subscribe>

        ' <Unsubscribe>
        Public Sub Unsubscribe()
            _unsubscriber?.Dispose()
        End Sub
        ' </Unsubscribe>

        ' <ObserverMethods>
        Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
            Console.WriteLine("Additional temperature data won't be transmitted.")
        End Sub

        ' OnError is informational; observers shouldn't treat it as an exception to handle.
        Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
        End Sub

        Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
            Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")

            If _last.HasValue Then
                Dim previous = _last.Value
                Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
                Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
            End If

            _last = value
        End Sub
        ' </ObserverMethods>
    End Class

End Namespace
' </All>

  1. Implement the three methods defined by System.IObserver: System.IObserver.OnNext, System.IObserver.OnError, and System.IObserver.OnCompleted.

    The System.IObserver.OnError and System.IObserver.OnCompleted methods can be stub implementations. The System.IObserver.OnError method shouldn't handle the passed System.Exception as an exception, and System.IObserver.OnCompleted can call the provider's System.IDisposable.Dispose implementation.

sample · csharpdocs/standard/events/snippets/shared/how-to-provider-observer/csharp/observer.csObserverMethods

csharp
// <All>
// <Subscribe>
// <ClassDeclaration>
namespace TemperatureSample;

public sealed class TemperatureReporter : IObserver<Temperature>
// </ClassDeclaration>
{
    private IDisposable? _unsubscriber;
    private Temperature? _last;

    public void Subscribe(IObservable<Temperature> provider)
    {
        ArgumentNullException.ThrowIfNull(provider);
        _unsubscriber = provider.Subscribe(this);
    }
    // </Subscribe>

    // <Unsubscribe>
    public void Unsubscribe() => _unsubscriber?.Dispose();
    // </Unsubscribe>

    // <ObserverMethods>
    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");

    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }

    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");

        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }

        _last = value;
    }
    // </ObserverMethods>
}
// </All>

sample · vbdocs/standard/events/snippets/shared/how-to-provider-observer/vb/observer.vbObserverMethods

vb
' <All>
' <Subscribe>
' <ClassDeclaration>
Namespace Global.TemperatureSample

    Public NotInheritable Class TemperatureReporter
        Implements IObserver(Of Temperature)
        ' </ClassDeclaration>

        Private _unsubscriber As IDisposable
        Private _last As Temperature?

        Public Sub Subscribe(provider As IObservable(Of Temperature))
            ArgumentNullException.ThrowIfNull(provider)
            _unsubscriber = provider.Subscribe(Me)
        End Sub
        ' </Subscribe>

        ' <Unsubscribe>
        Public Sub Unsubscribe()
            _unsubscriber?.Dispose()
        End Sub
        ' </Unsubscribe>

        ' <ObserverMethods>
        Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
            Console.WriteLine("Additional temperature data won't be transmitted.")
        End Sub

        ' OnError is informational; observers shouldn't treat it as an exception to handle.
        Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
        End Sub

        Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
            Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")

            If _last.HasValue Then
                Dim previous = _last.Value
                Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
                Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
            End If

            _last = value
        End Sub
        ' </ObserverMethods>
    End Class

End Namespace
' </All>

Complete example

The following example shows the complete source code for the TemperatureReporter class, which provides the System.IObserver implementation for a temperature monitoring application.

sample · csharpdocs/standard/events/snippets/shared/how-to-provider-observer/csharp/observer.csAll

csharp
// <All>
// <Subscribe>
// <ClassDeclaration>
namespace TemperatureSample;

public sealed class TemperatureReporter : IObserver<Temperature>
// </ClassDeclaration>
{
    private IDisposable? _unsubscriber;
    private Temperature? _last;

    public void Subscribe(IObservable<Temperature> provider)
    {
        ArgumentNullException.ThrowIfNull(provider);
        _unsubscriber = provider.Subscribe(this);
    }
    // </Subscribe>

    // <Unsubscribe>
    public void Unsubscribe() => _unsubscriber?.Dispose();
    // </Unsubscribe>

    // <ObserverMethods>
    public void OnCompleted() =>
        Console.WriteLine("Additional temperature data won't be transmitted.");

    // OnError is informational; observers shouldn't treat it as an exception to handle.
    public void OnError(Exception error) { }

    public void OnNext(Temperature value)
    {
        Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}");

        if (_last is Temperature previous)
        {
            TimeSpan elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime();
            Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}");
        }

        _last = value;
    }
    // </ObserverMethods>
}
// </All>

sample · vbdocs/standard/events/snippets/shared/how-to-provider-observer/vb/observer.vbAll

vb
' <All>
' <Subscribe>
' <ClassDeclaration>
Namespace Global.TemperatureSample

    Public NotInheritable Class TemperatureReporter
        Implements IObserver(Of Temperature)
        ' </ClassDeclaration>

        Private _unsubscriber As IDisposable
        Private _last As Temperature?

        Public Sub Subscribe(provider As IObservable(Of Temperature))
            ArgumentNullException.ThrowIfNull(provider)
            _unsubscriber = provider.Subscribe(Me)
        End Sub
        ' </Subscribe>

        ' <Unsubscribe>
        Public Sub Unsubscribe()
            _unsubscriber?.Dispose()
        End Sub
        ' </Unsubscribe>

        ' <ObserverMethods>
        Public Sub OnCompleted() Implements IObserver(Of Temperature).OnCompleted
            Console.WriteLine("Additional temperature data won't be transmitted.")
        End Sub

        ' OnError is informational; observers shouldn't treat it as an exception to handle.
        Public Sub OnError(error_ As Exception) Implements IObserver(Of Temperature).OnError
        End Sub

        Public Sub OnNext(value As Temperature) Implements IObserver(Of Temperature).OnNext
            Console.WriteLine($"The temperature is {value.Degrees}°C at {value.Date:g}")

            If _last.HasValue Then
                Dim previous = _last.Value
                Dim elapsed = value.Date.ToUniversalTime() - previous.Date.ToUniversalTime()
                Console.WriteLine($"   Change: {value.Degrees - previous.Degrees}° in {elapsed:g}")
            End If

            _last = value
        End Sub
        ' </ObserverMethods>
    End Class

End Namespace
' </All>