Click here to Skip to main content
Click here to Skip to main content

Handy wrapper class for thread-safe property access

, 21 Feb 2009
Rate this:
Please Sign up or sign in to vote.
A simple C# approach to thread-safe property access using Generics and type cast overloading.

Introduction

After writing C# threaded applications for a few years, I started to get bored of writing the synchronization code for each property I wanted to make thread-safe. If you want your class to manage the locking in a neat OOP encapsulated way, you must create locking variables and handle access to each property using get { } and set { }.

So came the idea of writing a simple wrapper class to do the dirty work for me. The Synchronized class is my first try at this, and can hopefully be further developed and extended.

I hope this can be of interest to some, and I really do hope to get some feedback to improve and extend my work. Smile | :)

Background

The basics to make this work are the use of Generics and type casting overloading. I suggest those who are not familiar with these concepts have a look at the .NET documentation or at one of the many good articles on the web.

Using the code

A typical C# approach to making properties thread-safe would be something like:

internal class MyThreadSafeCass
{
    // *** Lock ***
    private object PropertyLock = new object();
       
    // *** Property ***
    private int m_Property = 0;
        
    // *** Thread-safe access to Property using locking ***
    internal int Property
    {
        get
        {
            lock (PropertyLock)
            {
                return m_Property;
            }
        }
        set
        {
            lock (PropertyLock)
            {
                m_Property = value;
            }
        }
    }
}

Now, every thread in your application can access Property in a thread-safe way.

A class containing a few properties which must be made thread-safe would lead to writing quite some code, and decrease the overall neatness and readability.

Let's have a look at the Synchronized wrapper class. Its definition is quite simple:

// **********************************************
// *** Synchronized access wrapper class V1.0 ***
// **********************************************
// *** (C)2009 S.T.A. snc                     ***
// **********************************************
using System;

namespace STA.Threading
{

   internal class Synchronized<T>
   {
        // *** Locking ***
        private object m_ValueLock;

        // *** Value buffer ***
        private T m_Value;

        // *** Access to value ***
        internal T Value
        {
            get
            {
                lock (m_ValueLock)
                {
                    return m_Value;
                }
            }
            set
            {
                lock (m_ValueLock)
                {
                    m_Value = value;
                }
            }
        }

        // *******************
        // *** Constructor ***
        // *******************
        internal Synchronized()
        {
            m_ValueLock = new object();
        }

        internal Synchronized(T value)
        {
            m_ValueLock = new object();
            Value = value;
        }

        internal Synchronized(T value, object Lock)
        {
            m_ValueLock = Lock;
            Value = value;
        }

        // ********************************
        // *** Type casting overloading ***
        // ********************************
        public static implicit operator T(Synchronized<T> value)
        {
            return value.Value;
        }

    }
}

It makes use of Generics in order to be able to wrap any type, and overloads the implicit type casting to the wrapped type to allow a Synchronized object to be used in place of the wrapped type, where possible.

Now, we can use it to modify our test class:

using STA.Threading;

internal class MyThreadSafeCass
{
    // *** Thread-safe property ***
    internal Synchronized<int> Property = new Synchronized<int>(0);
}

Simple and neat.

What it does and what it does not

A property wrapped in a Synchronized class can be used in many common situations without needing any special syntax, but there are some exceptions. Here is a brief summary of its most common uses:

using STA.Threading;

...

// s1 <- 10
Synchronized<int> s1 = new Synchronized(10);
// s2 <- 0 (deafult for int)
Synchronized<int> s2 = new Synchronized();

s2 = 20; // ERROR: the '=' operator cannot be overloaded, thus
         // you cannot assign a value of type T directly to a
         // Synchronized<T> object

s2.Value = 20; // Valid: s2 <- 20

int sum = s1 + s2: // Valid: s1 and s2 are subject to implicit
                   // type casting to type T, so sum <- 30

s1++; // ERROR: I didn't find a way to overload the ++ and -- unary
      // operators because of a limitation with Generics: you cannot
      // use ++ or -- on generic type T. Hopefully this can be worked
      // around in some way in a future release. Maybe using "where
      // T : ..." ?

s1.Value++; // Valid: s1 <- 11

int TestFunction(int Value)
{
    return Value * 10;
}

int ret = TestFunction(s1); // Valid: s1 is subject to implicit type
                            // casting to type T, so ret <- 110

int.TryParse("50", out s1); // ERROR: out requires a modifiabile
                            // variable, so type casting is not
                            // possible

int.TryParse("50", out s1.Value); // ERROR: a property cannot be
                                  // used with out or ref

int tmp;
int.TryParse("50", out tmp);
s1.Value = tmp;              // Valid: s1 <- 50

...

The biggest limitation with this approach is it's no good with properties of complex types. For example, a Synchronized<DataDet> property will not render calls to the wrapped DataSet's properties or thread-safe methods.

Points of interest

I found the approach to implicit and explicit type casting overloading in C# particularly nice and efficient. Before this project, I only had experience with unary and binary operators overloading, and when I first started looking around in the documentation for some hint as to how to implement my idea, I doubted there would be such an easy solution.

History

  • 2009-02-21 - V1.0 - First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Moreno Airoldi
Software Developer S.T.A. snc
Italy Italy
Been coding since I was 10, started out on a Philips game console in pseudo-assembler and then moved on to a C64, C128, and lots of Amigas.
 
Been working in the field of software development for industrial automation since 1989.
 
Now I'm running a small software house together with my wonderful wife. We specialise in the development of software solutions for industrial automation, data acquisition, automated production management and ERP integration with factory automation.

Comments and Discussions

 
QuestionNice but... PinmemberCabbi1-Apr-14 2:04 
AnswerRe: Nice but... PinmemberMoreno Airoldi2-Apr-14 7:06 
Generalis there any performance issues , is this a replacement for volatile fields. PinmemberBAIJUMAX3-Apr-09 16:53 
AnswerRe: is there any performance issues , is this a replacement for volatile fields. [modified] PinmemberMoreno Airoldi4-Apr-09 1:22 
GeneralRe: is there any performance issues , is this a replacement for volatile fields. [modified] PinmemberBAIJUMAX4-Apr-09 6:20 
GeneralRe: is there any performance issues , is this a replacement for volatile fields. PinmemberMoreno Airoldi4-Apr-09 23:41 
GeneralRe: is there any performance issues , is this a replacement for volatile fields. PinmemberBAIJUMAX5-Apr-09 19:32 
GeneralInstance class property Pinmemberonidsuit25-Mar-09 16:26 
GeneralRe: Instance class property PinmemberMoreno Airoldi26-Mar-09 2:16 
GeneralQuestion PinmemberPIEBALDconsult22-Feb-09 12:02 
GeneralRe: Question PinmemberMoreno Airoldi23-Feb-09 1:13 
GeneralRe: Question Pinmemberonidsuit31-Mar-09 14:54 
GeneralRe: Question PinmemberMoreno Airoldi1-Apr-09 0:23 
GeneralRe: Question Pinmemberonidsuit1-Apr-09 16:02 
General[MethodImpl(MethodImplOptions.Synchronized)] PinmemberOmer Mor22-Feb-09 6:46 
GeneralRe: [MethodImpl(MethodImplOptions.Synchronized)] PinmemberMoreno Airoldi22-Feb-09 11:04 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140709.1 | Last Updated 21 Feb 2009
Article Copyright 2009 by Moreno Airoldi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid