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

INotifyPropertyChanged and Beyond - Part I

By , 7 May 2007
 
Screenshot - screenshot.png

Introduction

The INotifyPropertyChanged interface provides a standard event for objects to notify clients that one of its properties has changed. This is helpful for data binding (as described in this article), since it allows for bound controls to update their display based on changes made directly to the underlying object. While this event serves its purpose, there is still room for improvement.

This article goes over some improvements and extensions to this interface. We will start with easing the use of INotifyPropertyChanged and add new features as we go along. A new interface, called IPropertyNotification, will be used in order to extend the INotifyPropertyChanged interface.

IPropertyNotification Interface and a Base Class

To begin, the IPropertyNotification interface will simply be defined as:

public interface IPropertyNotification : INotifyPropertyChanged {
    // No members
}

We will then define a base class as follows, which will allow our derived objects to easily utilize this interface:

/// <summary>
/// This class implements the <see cref="T:IPropertyNotification"/>
/// interface and provides helper methods for derived classes.
/// </summary>
public class PropertyNotificationObject : IPropertyNotification {
    #region IPropertyNotification

    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    [field:NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;

    #endregion // IPropertyNotification

    #region Methods

    /// <summary>
    /// Raises the <see cref="E:PropertyChanged"/> event.
    /// </summary>
    /// <param name="propertyName">
    /// Name of the property that changed.
    /// </param>
    protected void OnPropertyChanged(String propertyName) {
        PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
        OnPropertyChanged(e);
    }

    /// <summary>
    /// Raises the <see cref="E:PropertyChanged"/> event.
    /// </summary>
    /// <param name="e">
    /// The <see cref="PropertyChangedEventArgs"/> instance
    /// containing the event data.
    /// </param>
    protected void OnPropertyChanged(PropertyChangedEventArgs e) {
        PropertyChangedEventHandler temp = this.PropertyChanged;
        if (null != temp)
            temp(this, e);
    }

    #endregion // Methods
}

As you can see, the base class allows derived classes to easily call the PropertyChanged event. As shown here:

/// <summary>
/// This class is used to test the functionality of
/// <see cref="T:PropertyNotificationObject"/> and
/// <see cref="T:IPropertyNotification"/>.
/// </summary>
public class TestObject : PropertyNotificationObject {
    #region Properties

    /// <summary>
    /// Holds the name.
    /// </summary>
    private String name = String.Empty;

    /// <summary>
    /// Gets or sets the name.
    /// </summary>
    /// <value>The name.</value>
    public String Name {
        get {
            return this.name;
        }
        set {
            if (false == Object.Equals(value, this.name)) {
                this.name = value;
                OnPropertyChanged("Name");
            }
        }
    }

    #endregion // Properties
}

Now that the base class and interface are defined, we will start to add some new functionality.

What Changed?

When the PropertyChanged event is fired, only the name of the changed property is provided. In certain cases, it would be useful to know the previous value and the new value of the property. The new value could be obtained using the information currently available in the event. Specifically, reflection could be used on the sender (assuming the sender is the object that changed) to get the value of the property with the name specified by the event. This tends to get messy though and does not allow us to get the previous value.

Instead of using reflection, we will implement a class derived from PropertyChangedEventArgs that will carry the old and new values. This class is shown below:

/// <summary>
/// This class extends <see cref="T:PropertyChangedEventArgs"/> and
/// allows for storing the old and new values of the changed property.
/// </summary>
public class PropertyNotificationEventArgs : PropertyChangedEventArgs {
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="PropertyNotificationEventArgs"/> class.
    /// </summary>
    /// <param name="propertyName">
    /// The name of the property that is associated with this
    /// notification.
    /// </param>
    public PropertyNotificationEventArgs(String propertyName)
        : this(propertyName, null, null) {
        // No-op
    }

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="PropertyNotificationEventArgs"/> class.
    /// </summary>
    /// <param name="propertyName">
    /// The name of the property that is associated with this
    /// notification.
    /// </param>
    /// <param name="oldValue">The old value.</param>
    /// <param name="newValue">The new value.</param>
    public PropertyNotificationEventArgs(String propertyName,
        Object oldValue, Object newValue)
        : base(propertyName) {
        this.oldValue = oldValue;
        this.newValue = newValue;
    }

    #endregion // Constructors

    #region Properties

    /// <summary>
    /// Holds the new value of the property.
    /// </summary>
    private Object newValue;

    /// <summary>
    /// Gets the new value of the property.
    /// </summary>
    /// <value>The new value.</value>
    public Object NewValue {
        get {
            return this.newValue;
        }
    }

    /// <summary>
    /// Holds the old value of the property.
    /// </summary>
    private Object oldValue;

    /// <summary>
    /// Gets the old value of the property.
    /// </summary>
    /// <value>The old value.</value>
    public Object OldValue {
        get {
            return this.oldValue;
        }
    }

    #endregion // Properties
}

In order to use this new class, we must modify our base class and our test object, as shown here:

/// <summary>
/// This class implements the <see cref="T:IPropertyNotification"/>
/// interface and provides helper methods for derived classes.
/// </summary>
public class PropertyNotificationObject : IPropertyNotification {
    // ... Existing code ...

    #region Methods

    /// <summary>
    /// Raises the <see cref="E:PropertyChanged"/> event.
    /// </summary>
    /// <param name="propertyName">
    /// Name of the property that changed.
    /// </param>
    /// <param name="oldValue">The old value.</param>
    /// <param name="newValue">The new value.</param>
    protected void OnPropertyChanged(String propertyName,
        Object oldValue, Object newValue) {
        PropertyNotificationEventArgs e = 
	new PropertyNotificationEventArgs(propertyName,
            oldValue, newValue); // ** Pass in the old and new value
        OnPropertyChanged(e);
    }

    // ... Existing code ...

    #endregion // Methods
}

/// <summary>
/// This class is used to test the functionality of
/// <see cref="T:PropertyNotificationObject"/> and
/// <see cref="T:IPropertyNotification"/>.
/// </summary>
public class TestObject : PropertyNotificationObject {
    #region Properties

    // ... Existing code ...

    /// <summary>
    /// Gets or sets the name.
    /// </summary>
    /// <value>The name.</value>
    public String Name {
        // ... Existing code ...
        set {
            if (false == Object.Equals(value, this.name)) {
                String oldValue = this.name; // ** Save the old value
                this.name = value;
	       // ** Pass in the old and new value
                OnPropertyChanged("Name", oldValue, this.name); 
            }
        }
    }

    #endregion // Properties
}

One problem with this approach is that clients must use the "is" or "as" operators to determine if the given PropertyChangedEventArgs is actually an instance of PropertyNotificationEventArgs. We could overcome this problem by creating a new event in our IPropertyNotification interface which takes an instance of PropertyNotificationEventArgs, but then we lose the seamless integration with clients that already support INotifyPropertyChanged.

Cancel A Change

There are many scenarios where simply getting an event after a property has changed is sufficient. There are also many scenarios where a cancellable event before a property has changed is required (e.g. Source Control, Validation, etc). In order to better support these types of scenarios, we will add a PropertyChanging event to our interface. First, we need to define a class derived from PropertyNotificationEventArgs that allows us to cancel the event. This class is shown below:

/// <summary>
/// This class extends <see cref="T:PropertyNotificationEventArgs"/> and
/// allows for cancelling of the associated event.
/// </summary>
public class CancelPropertyNotificationEventArgs : PropertyNotificationEventArgs {
    #region Constructors

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="CancelPropertyNotificationEventArgs"/> class.
    /// </summary>
    /// <param name="propertyName">
    /// The name of the property that is associated with this
    /// notification.
    /// </param>
    public CancelPropertyNotificationEventArgs(String propertyName)
        : base(propertyName) {
        // No-op
    }

    /// <summary>
    /// Initializes a new instance of the
    /// <see cref="CancelPropertyNotificationEventArgs"/> class.
    /// </summary>
    /// <param name="propertyName">
    /// The name of the property that is associated with this
    /// notification.
    /// </param>
    /// <param name="oldValue">The old value.</param>
    /// <param name="newValue">The new value.</param>
    public CancelPropertyNotificationEventArgs(String propertyName,
        Object oldValue, Object newValue)
        : base(propertyName, oldValue, newValue) {
        // No-op
    }

    #endregion // Constructors

    #region Properties

    /// <summary>
    /// Holds a value indicating whether the associated event should be
    /// cancelled.
    /// </summary>
    private Boolean cancel = false;

    /// <summary>
    /// Gets or sets a value indicating whether the associated event should
    /// be cancelled.
    /// </summary>
    /// <value>
    /// <c>true</c> if the event should be cancelled; otherwise, <c>false</c>.
    /// </value>
    public Boolean Cancel {
        get {
            return this.cancel;
        }
        set {
            this.cancel = value;
        }
    }

    #endregion // Properties
}

Next we will add a new event to our interface and the associated helper methods to our base class as shown here:

/// <summary>
/// Notifies clients that a property value is changing or changed.
/// </summary>
public interface IPropertyNotification : INotifyPropertyChanged {
    #region Events

    /// <summary>
    /// Occurs when a property value is changing.
    /// </summary>
    event PropertyChangingEventHandler PropertyChanging;

    #endregion // Events
}
/// <summary>
/// This class implements the <see cref="T:IPropertyNotification"/>
/// interface and provides helper methods for derived classes.
/// </summary>
public class PropertyNotificationObject : IPropertyNotification {
    #region IPropertyNotification

    // ... Existing code ...

    /// <summary>
    /// Occurs when a property value is changing.
    /// </summary>
    [field: NonSerialized]
    public event PropertyChangingEventHandler PropertyChanging;

    #endregion // IPropertyNotification

    #region Methods

    // ... Existing code ...

    /// <summary>
    /// Raises the <see cref="E:PropertyChanging"/> event.
    /// </summary>
    /// <param name="propertyName">
    /// Name of the property that is changing.
    /// </param>
    /// <param name="oldValue">The old value.</param>
    /// <param name="newValue">The new value.</param>
    /// <returns><c>true</c> if the change can continue; 
    /// otherwise <c>false</c>.</returns>
    protected Boolean OnPropertyChanging(String propertyName,
        Object oldValue, Object newValue) {
        CancelPropertyNotificationEventArgs e = 
			new CancelPropertyNotificationEventArgs(propertyName,
            oldValue, newValue);
        OnPropertyChanging(e);
        return !e.Cancel;
    }

    /// <summary>
    /// Raises the <see cref="E:PropertyChanging"/> event.
    /// </summary>
    /// <param name="e">
    /// The <see cref="CancelPropertyNotificationEventArgs"/> instance
    /// containing the event data.
    /// </param>
    protected void OnPropertyChanging(CancelPropertyNotificationEventArgs e) {
        PropertyChangingEventHandler temp = this.PropertyChanging;
        if (null != temp)
            temp(this, e);
    }

    #endregion // Methods
}

Finally, we can hook up the new event to our test object like so:

/// <summary>
/// This class is used to test the functionality of
/// <see cref="T:PropertyNotificationObject"/> and
/// <see cref="T:IPropertyNotification"/>.
/// </summary>
public class TestObject : PropertyNotificationObject {
    #region Properties

    // ... Existing code ...

    /// <summary>
    /// Gets or sets the name.
    /// </summary>
    /// <value>The name.</value>
    public String Name {
        // ... Existing code ...
        set {
            if (false == Object.Equals(value, this.name)) {
                if (true == OnPropertyChanging("Name", this.name, value)) 
	        { // ** Call Changing event
                    String oldValue = this.name;
                    this.name = value;
                    OnPropertyChanged("Name", oldValue, this.name);
                }
            }
        }
    }

    #endregion // Properties
}

Using this new method, it is now possible to cancel changes on an instance of TestObject. This includes changes from the PropertyGrid, a data bound control, or from direct access.

Boiler-plate Set Code

We have abstracted out the code from the Property set method into a helper method in the base class. While this may handle most cases, there will still be instances where this helper method will not work. The new helper method is shown here:

/// <summary>
/// This method is used to set a property while firing associated
/// PropertyChanging and PropertyChanged events.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="propertyField">The property field.</param>
/// <param name="value">The value.</param>
protected void SetProperty<T>(String propertyName, ref T propertyField,
    T value) {
    if (false == Object.Equals(value, propertyField)) {
        if (true == OnPropertyChanging(propertyName, propertyField, value)) {
            T oldValue = propertyField;
            propertyField = value;
            OnPropertyChanged(propertyName, oldValue, propertyField);
        }
    }
}

And this is how it is used by the TestObject:

/// <summary>
/// This class is used to test the functionality of
/// <see cref="T:PropertyNotificationObject"/> and
/// <see cref="T:IPropertyNotification"/>.
/// </summary>
public class TestObject : PropertyNotificationObject {
    #region Properties

    // ... Existing code ...

    /// <summary>
    /// Gets or sets the name.
    /// </summary>
    /// <value>The name.</value>
    public String Name {
        // ... Existing code ...
        set {
            SetProperty<String>("Name", ref this.name, value);
        }
    }

    #endregion // Properties
}

Wrap Up

We have started to build base code that will allow our applications to have finer control over changes to our objects. These events are very helpful when using third-party controls, which do not allow similar control.

The demo application contains all the completed code from this article and a simple example that shows it in action.

In the next part of this article, we will tackle the following improvements:

  1. Propagation support – This allows an object to propagate its PropertyChanged/PropertyChanging events to its parent, when objects are organized in a hierarchical fashion (e.g. parent/child). This allows client applications to hook up a single event handler in order to receive any change notifications.
  2. Batch Change support – When lots of properties are being updated, it may be desirable to group these changes into a single event. In this case, we would like to know all the properties that changed and how they changed.
  3. Event suppression – There may be cases where we would like to suppress these events altogether. So we will add support for turning off the events.

History

  • 7th May, 2007: Initial post

License

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

About the Author

TJoe
Chief Technology Officer SQL Farms, Inc.
United States United States
Member
My name is Tom Goff and I have been working as a Software Engineer for over 15 years. Over my career, I have primarily focused on Windows programming with C++ and C#. I have also worked extensively with Microsoft SQL Server over the past 6 years.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow to implement INotifyPropertyChanged right?memberjbe822418 Apr '09 - 11:03 
Further considerations about implementing the INotifyPropertyChanged interface are discussed here:

"Best Practices: How to implement INotifyPropertyChanged right?"

You might find the discussion useful too.
GeneralNo "Notify Test" appears in the output of this project executed in Visual C#.NET 2005 Express!!?? [modified]memberSctt H. Chang19 Nov '08 - 4:42 
Hi TJoe,
I read your article to learn "INotifyPropertyChanged" and downloaded your demo project to my PC. I executed your "NotifyTest" project in my Visual C#.NET 2005 Express. It generated the output of "Form1" with 'Misc' and 'Name'. If I clicked on the 'Misc' or 'Name', I saw 'Misc' or 'Name' appeared in the left-hand-side bottom. But I did not get the box of "Notify Test" Would you like to change the property 'Name' from 'Testing' to 'More Testing? |Yes| |No| in my computer screen!!?? I am new in the C# programming and "INotifyPropertyChanged" is too complicated and hard for me to understand and use it. Could you please tell me why I did not get the "Notify Test" box and how to fix it?
Thanks in advance,
Scott Chang
 
modified on Wednesday, November 19, 2008 10:51 AM

QuestionCancelling the Event?memberSteveC-A97 Oct '08 - 11:01 
I don't understand how to set, object wide from a single call, "do not implement INotifyPropertyChanged", and then from another single call "now enable INotifyPropertyChanged". I am trying to read a lot of database data into my objects and don't want INotifyPropertyChanged to fire until all of them have completed loading.
 
Thanks.
GeneralExcellent Articlemembermathewrajiv4 May '08 - 20:33 
This is an excellent article....
I would really like to know how the property changed event can be used in the MVP pattern where the binidng class is in the presenter and a custom binding class would gather data from entities which have Inotifyproperrty.... implemented...
for eg
 
class CustombindingClass
{
public AssignmentPersonName
{
get{ return Assignment.Person.FirstName}
set {Assignment.Person.FirstName = value;}
}

public Assignment{get;set}

}
 
class Presenter
{
CustomBindingClass customBindingClass= new CustomBindingClass();
customBindingClass.Assignment= Assignment.GetCurrentAssignment();
}
 
Class View
{
 
public void FillGridWithCustomBindingClassData()
{
datagridview1.datasource= CustomBindingClass.CustomBindingClass;
}
}
 

 
here the Class Assignment : INotifyPropertyChanged
 
When the Grid is bound to class "CustombindingClass" and the property of "Assignment" entity changes, how to rebind the grid again.
GeneralNicememberelektrowolf25 Apr '08 - 7:30 
Nice work, but it's the same problem as with dependency properties: The container class musst be derived from PropertyNotificationObject. This makes it impossible to include your class into a larger class structure Frown | :(
GeneralRe: Nicememberelektrowolf25 Apr '08 - 7:35 
And something else: Why do you initialize the string field with String.Empty? "" is much more readable although even this is not necessary. And do you work with .NET 3.5? Then you can use automatic properties:
 

public string MyProp { get; set; }

equals

private string _myProp;
public string MyProp
{
get { return this._myProp; }
set { this._myProp = value; }
}

If you want a readonly property, you can simply define the setter as private.
GeneralFor serialization supportingmemberYanhua Zhang21 Jan '08 - 23:12 
Tom,
Excelent and smart job.
I have used your pattern to desing my data entity, which support advanced event notification mechanism.
One problem is that I need make my data entities serializable. When outside noserializable object bind the entity object's PropertyChanged or Perperty changing event, I can't serialize my entity to disk file directly.
 
I do following change to PropertyNotificationObject.cs class:
        /// <summary>
        /// Out event, registered by outside objects.
        /// Occurs when a property value changes.
        /// </summary>
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;
 
        /// <summary>
        /// Out event, registered by outside objects.
        /// Occurs when a property value is changing.
        /// </summary>
        [field: NonSerialized]
        public event PropertyChangingEventHandler PropertyChanging;
        
        ....
 
        /// <summary>
        /// Inner event,Occurs when a property value changes.
        /// </summary>
        internal event PropertyChangedEventHandler InnerPropertyChanged;
 
        /// <summary>
        /// Inner event,Occurs when a property value is changing.
        /// </summary>
        internal event PropertyChangingEventHandler InnerPropertyChanging;
        ....
 
        protected void AddPropertyEventHandlers(Object obj)
        {
            PropertyNotificationObject propNotify = obj as PropertyNotificationObject;
            if (null != propNotify)
            {
                propNotify.InnerPropertyChanged += new PropertyChangedEventHandler(SubObject_PropertyChanged);
                propNotify.InnerPropertyChanging += new PropertyChangingEventHandler(SubObject_PropertyChanging);
            }
        }
        ....
        protected void RemovePropertyEventHandlers(Object obj)
        {
            IPropertyNotification propNotify = obj as IPropertyNotification;
            if (null != propNotify)
            {
                //propNotify.PropertyChanged -= new PropertyChangedEventHandler(SubObject_PropertyChanged);
                //propNotify.PropertyChanging -= new PropertyChangingEventHandler(SubObject_PropertyChanging);
                InnerPropertyChanged -= new PropertyChangedEventHandler(SubObject_PropertyChanged);
                InnerPropertyChanging -= new PropertyChangingEventHandler(SubObject_PropertyChanging); ;
            }
        }
 
        protected void OnPropertyChanged(Object sender,
            PropertyChangedEventArgs e)
        {
            if (true == this.PropertyEventsSuspended)
                return;
 
            PropertyChangedEventHandler temp = this.InnerPropertyChanged;
            if (null != temp)
                temp(sender, e);
 
            PropertyChangedEventHandler outEvent = this.PropertyChanged;
            if (null != outEvent)
            {
                if (sender is ListPropertyNotificationObject)
                {
                    // If is list operation notification, replace sender with the 
                    // container of the list.
                    outEvent(((ListPropertyNotificationObject)sender).parentEntity, e);
                }
                else
                {
                    outEvent(sender, e);
                }
            }
        }
 
        protected void OnPropertyChanging(Object sender,
            CancelPropertyNotificationEventArgs e)
        {
 
            // If don't suspend event, trigger out side event.
            if (!this.PropertyEventsSuspended)
            {
                PropertyChangingEventHandler temp = this.InnerPropertyChanging;
                if (null != temp)
                    temp(sender, e);
 
                PropertyChangingEventHandler outEvent = this.PropertyChanging;
                if (null != outEvent)
                    outEvent(sender, e);
            }
        }
 

 

In the upper code, define 2 new inner event handler <code>InnerPropertyChanged</code> and 
<code>InnerPropertyChanging</code>, and mark <code>[field: NonSerialized]</code> to public
 out properties <code>PropertyChanged </code> and <code>PropertyChanging</code>. 
 
The inner perperty event will support build event relation among entities for event propagate and collection operation event. 
And the inner perperty can be serialized. that means when persit or restor a root entity, 
all inner event registration among root entities and sub child entities can be keeped. On the same time, 
outside object instance register entitie's <code>PropertyChanged </code> event will not prevent the 
entity's serialization.
 
thanks.
Yanhua
 
Yanhua
GeneralINotifyPropertyChanging in .Net 3.5memberTJoe18 Oct '07 - 8:10 
FYI, there is a new INotifyPropertyChanging[^] interface in .Net 3.5.
 
Take care,
Tom
 
-----------------------------------------------
Check out my blog at http://tjoe.wordpress.com

GeneralFixing DeserializationmemberMike Gavaghan22 Jul '07 - 7:44 
Good article. I think the SDK designers were shortsighted omitting the "old value" property on the PropertyChanged event. Nice extension.
 
One catch - if you add INotifyPropertyChanged instances to a BindingList, you'll get ListChanged/ItemChanged events as expected. But, if you try to serialize and deserialize the list (for example, because you're sending the object over a remote connection), you lose the events. It's because the event handlers aren't serializable and BindingList fails to hook them back up.
 
This demonstrates the problem: Demonstrating BindingList Serialization Bug
 
This fixes it: Fixing BindingList Deserialization

 
Mike Gavaghan, Talk Nerdy To Me

GeneralRe: Fixing DeserializationmemberTJoe22 Jul '07 - 9:42 
Thanks for the insight into the BindingList. This is definitely helpful.
 
For others interested, Mike has kindy summarized his findings here[^].
 
Take care,
Tom
 
-----------------------------------------------
Check out my blog at http://tjoe.wordpress.com

Questionfalse == ... false?memberSteve Hansen7 May '07 - 21:51 
Nice article, got my 5, INotifyPropertyChanged is often overlooked while it's still very powerfull(databinding will use it for example to update the UI if you change a property)
 
But what's up with:
if (false == Object.Equals(value, propertyField))
I never understand why people type == false or even == true. That line can easily be changed to:
if (!Equals(value, propertyField))
AnswerRe: false == ... false?memberTJoe8 May '07 - 2:20 
I'm not sure how I got into the habit of using "false ==" and "true ==". Smile | :) I realize it's a bit redundant.
 
I know I put constants on the left side of the comparisons because of my C++ days. Where you could type "if (i = 0)" by mistake, but where that is perfectly legal.
 
For static methods, I usually prefix it with the class that it's defined in (in this case Object). Although, I'd be hard pressed to put up a good argument for any of these styles.
 
This does remind of a product idea I had (not sure if it's an original idea though). The idea is that the editor could/would format the source code so that it is displayed in "your coding style". In addition, the source code could/would be saved in "your company's coding style". With this product, you wouldn't have even see the "false == " Wink | ;-)
 
But, like most things, there are many challanges related to doing this Smile | :)
 
Take care,
Tom
 
-----------------------------------------------
Check out my blog at http://tjoe.wordpress.com

GeneralRe: false == ... false?memberelektrowolf25 Apr '08 - 7:38 
TJoe wrote:
Where you could type "if (i = 0)" by mistake, but where that is perfectly legal.

It still is in C#. But I don't really understand why putting
false ==
or
true ==
on the left side helps you to remove this mistake?? If you simply use
if (condition)
or
if (!condition)
there would be no risk of this mistake either.

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 7 May 2007
Article Copyright 2007 by TJoe
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid