Wonko the Sane
Wonko the Sane

Reputation: 10823

C# Thread-safe Extension Method

I may be waaaay off, or else really close. Either way, I'm currently SOL. :)

I want to be able to use an extension method to set properties on a class, but that class may (or may not) be updated on a non-UI thread, and derives from a class the enforces updates to be on the UI thread (which implements INotifyPropertyChanged, etc).

I have a class defined something like this:

public class ClassToUpdate : UIObservableItem
{
    private readonly Dispatcher mDispatcher = Dispatcher.CurrentDispatcher;
    private Boolean mPropertyToUpdate = false;

    public ClassToUpdate() : base()
    {
    }

    public Dispatcher Dispatcher
    {
        get { return mDispatcher; }
    }

    public Boolean PropertyToUpdate
    {
        get { return mPropertyToUpdate; }
        set { SetValue("PropertyToUpdate", ref mPropertyToUpdate, value; }
    }
}

I have an extension method class defined something like this:

static class ExtensionMethods
{
    public static IEnumerable<T> SetMyProperty<T>(this IEnumerable<T> sourceList,
                                                  Boolean newValue)
    {
       ClassToUpdate firstClass = sourceList.FirstOrDefault() as ClassToUpdate;

       if (firstClass.Dispatcher.Thread.ManagedThreadId != 
           System.Threading.Thread.CurrentThread.ManagedThreadId)
        {
            // WHAT GOES HERE?
        }
        else
        {
            foreach (var classToUpdate in sourceList)
            {
               (classToUpdate as ClassToUpdate ).PropertyToUpdate = newValue;
               yield return classToUpdate;
            }
        }
    }
}

Obviously, I'm looking for the "WHAT GOES HERE" in the extension method.

Thanks, wTs

Upvotes: 3

Views: 2444

Answers (2)

Wonko the Sane
Wonko the Sane

Reputation: 10823

Just to clean up a couple of small typos (and hopefully not add my own) in the above example, here is a final solution to the example.

public static IEnumerable<T> SetMyProperty<T>(this IEnumerable<T> sourceList, 
    bool newValue) where T : ClassToUpdate
{
    Action<T> setProperty = t => t.PropertyToUpdate = newValue;

    foreach(var t in sourceList)
    {
        if (t.Dispatcher.CheckAccess())
        {
            setProperty(t);
        }
        else
        {
            t.Dispatcher.Invoke(setProperty, new object[] { t });
        }

        yield return t;
    }
}

Upvotes: 0

Thomas Levesque
Thomas Levesque

Reputation: 292605

// WHAT GOES HERE?

mDispatcher.Invoke(new Action(() => sourceList.SetMyProperty(newValue)));

As a side note, if you need to check whether the current thread has access to the UI, you don't need to compare thread ids. You just need to call the CheckAccess method :

if (firstClass.Dispatcher.CheckAccess())
{
    ...
}

For some reason, this methods is hidden in Intellisense... no idea why


UPDATE

OK, my answer wasn't totally accurate... you still need to yield return each item of the collection, and Invoke doesn't do it. Here's another version of your method :

public static IEnumerable<T> SetMyProperty<T>(this IEnumerable<T> sourceList, bool newValue)
    where T : ClassToUpdate
{
    Action<T> setProperty = t => t.PropertyToUpdate = newValue;

    foreach(var t in sourceList)
    {
        if (t.Dispatcher.CheckAccess())
        {
            action(t);
        }
        else
        {
            t.Dispatcher.Invoke(action, new object[] { t });
        }
    }
}

Note that I added a constaint on the generic type parameter, and I removed the casts (the way you were doing it, generics didn't bring any benefit)

Upvotes: 1

Related Questions