Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Property dependency generation in Visual Studio

0.00/5 (No votes)
20 Jan 2009 1  
A VS add-in to analyse property dependencies in classes.

Contents

Introduction

This article shows how to overcome one of the limitations of property change notifications – namely, their inability to recognise property dependencies and propagate them when several properties depend on one another. The end result is a Visual Studio add-in that uses static analysis (via the Cecil library) to identify property dependencies and generate the suitable code-behind.

The Problem

Consider a class describing a person who can vote. The voter has two properties – Age and CanVote, the assumption being that you need to be at least 16 to vote. Here’s how it would look in code:

class Voter
{
  private int age;

  public int Age
  {
    get { return age; }
    set { age = value; }
  }

  public bool CanVote
  {
    get
    {
      return Age >= 16;
    }
  }
}

Imagine now that you want to bind both properties to some sort of UI. You change your class to implement the INotifyPropertyChanged interface, and end up with the following:

class Voter : INotifyPropertyChanged
{
  private int age;

  public int Age
  {
    get { return age; }
    set
    {
      age = value;
      NotifyPropertyChanged("Age");
    }
  }

  public bool CanVote
  {
    get
    {
      return Age >= 16;
    }
  }

  // event and firing code omitted
}

Now, you can bind the Age property to the UI, or just listen to its changes. But, what about CanVote? After all, when Age changes, CanVote should also change. But, there isn’t any place for us to put the NotifyPropertyChanged() call, because CanVote has no setter. What can we do?

In a simple scenario, we would simply alter the Age property to do notifications for both, as follows:

public int Age
{
  get { return age; }
  set
  {
    age = value;
    NotifyPropertyChanged("Age");
    NotifyPropertyChanged("CanVote");
  }
}

As you can see, such a solution is error-prone and doesn’t scale. Without some automation mechanism (e.g., code generation), it would be hell to maintain. Also, it’s simply bad practice: you end up having properties which concern themselves with other properties’ notifications. That’s not right! But, how can we fix it?

The Solution

Class Changes

Let’s hypothesize for a moment that we already have a property dependency graph. How would we implement it in C# with respect to our Voter class? Well, the first thing to do is declare the class partial and remove the full NotifyPropertyChanged() implementation, leaving instead just a partial definition. (We also remove the spurious notification.)

partial class Voter
{
  private int age;

  public int Age
  {
    get { return age; }
    set
    {
      age = value;
      NotifyPropertyChanged("Age");
    }
  }

  public bool CanVote
  {
    get
    {
      return Age >= 16;
    }
  }

  partial void NotifyPropertyChanged(string propertyName);
}

So, we now have a slimmer Voter class, from which we removed the NotifyPropertyChanged() implementation, since it’s not powerful enough to handle dependencies. Also, I removed the PropertyChanged event – you’ll see it in a moment. Now, since we made the class partial, let’s think about what our code-behind class would contain?

Code-Behind Class

The first obvious thing is some sort of a dependency list, specifying which property depends on which. My approach is to simply define it as a Dictionary:

// A dictionary of dependencies such that key is the
// independent variable and value is a string[] of dependent variables.
protected Dictionary<string, string[]> dependencies;

Now that we have a Dictionary, we also define a function for initializing it with our data:

// Since a Dictionary cannot be initialized inline, it has to be
// initialized conditionally.
private void InitializeDependencies()
{
  dependencies = new Dictionary<string, string[]>();
  dependencies.Add("Age", new [] { "CanVote" });
}

Finally, we implement the partial NotifyPropertyChanged() function to take advantage of our Dictionary. In this implementation, we simply lazy-init the Dictionary, then use it whenever we need it. Please note there is no circular dependency check.

// This notification has to be invoked by any property that
// needs to notify users of its changes.
partial void NotifyPropertyChanged(string propertyName)
{
  if (dependencies == null) InitializeDependencies();

  if (PropertyChanged != null)
  {
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    if (dependencies.ContainsKey(propertyName))
      foreach (string s in dependencies[propertyName])
        NotifyPropertyChanged(s);
  }
}

Finally, we also add the event to this class. After all, our original Voter class no longer implements the INotifyPropertyChanged – the partial part does!

Analyser

What I described just now may seem like magic. Who, apart from the developer, would know that the CanVote property can depend on the Age property? In actual fact, this information is compiled into IL, and we can extract it using a library like Cecil1. The source code for the analyser is attached, but here is a brief description of how the analyser finds the information:

  1. First, we locate the compiled assembly, and determine the class and namespace of the selected file.
  2. Then, we get a list of dependencies by looking at the IL and identifying which property is using which other property. Property gets are easy to find – all we have to do is look for methods starting with get_.
  3. When we get a list of dependencies, we need to reverse it, since we need a list of effects. What I mean is that, initially, we get a list of the types where A depends on B (A ← B), but our dictionary needs lists specifying that B affects A (B → A).
  4. The resulting data is emitted into a code-behind of the original class using a template.

Conclusion

This article has demonstrated how to auto-generate property dependency maps. Unlike my previous articles2 3, this generator uses IL analysis rather than source code analysis. Admittedly, such analysers are much less suited for the kind of code generation I did here, but they are powerful, and useful for analyzing assemblies written in languages other than C#.

References

  1. A free library for IL parsing; available at http://www.mono-project.com/Cecil
  2. A tool for making C# decorators/proxies, http://www.codeproject.com/KB/codegen/decorators.aspx
  3. A tool for making C# visitors, http://www.codeproject.com/KB/codegen/visitortool.aspx

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