Property Default Value

Jun 18, 2010 at 6:58 AM

I have a question about this code in the ViewModelBase:

 protected T Get<T>(string name, T defaultValue)
        {
            if (_values.ContainsKey(name))
            {
                return (T)_values[name];
            }
            
            return defaultValue;
        }

If the default value is a reference type would you not lose any changes you made to the default value because it is not assigned to the _values Dictionary?  So if I had Get(Of Map)("MyMap", New Map()).AMapProperty = "Test" and then I called the Get method again, it would return the blank default value again and the "AMapProperty" would not be set.  The solution would be to add the default value to _values Dictionary before returning it or am I mistaken?

Thanks,

Gene 

Coordinator
Jun 18, 2010 at 11:07 AM

Gene,

I supposes this is a matter of semantics.  When I hear "default value", I interpret that to mean "If it has not been set, use this.".  I that case, returning a new Map every time is OK with me.  It is a default value.  You haven't set it, but we don't want to return null.  Putting it in the _values collection implies it has actually been set.

This, however, is different to me than "initial value".  Having an initial value, to me, means that it has been set.  What if that method changed to something like this:

protected T Get<T>(string name, T defaultValue = default(T), object initialValue = null)
{
    if (_values.ContainsKey(name))
    {
        return (T)_values[name];
    }

    if(initialValue != null)
    {
        Set(name, (T)initialValue);
        return Get<T>(name);
    }
            
    return defaultValue;
}

Then, you can call it like this:

Get<Map>(() => MyMap, initialValue: new Map());

Would that achieve the semantics that we both want?

B

Coordinator
Jun 18, 2010 at 11:11 AM

A few more thoughts on this:

By doing it either way (your way, or mine), we are constructing a new Map EVERY time get is called.  Almost every time, this new Map() will be ignored.  This feels a bit like some wasted effort.  What do you think?

Also, I would question if you need a notifiable property here at all?  Do you really want to notify the UI when the reference to the Map has changed?  Usually, you have something like an ObservableCollection that notifies when the contents change, but you don't care to tell when the reference changes.  You may have a need for this... I just thought I should ask.

Thanks again,

Brian

Coordinator
Jun 18, 2010 at 1:40 PM

One last thought... bear with me.  I think I like this the best.  Tell me what you think:

protected T Get<T>(string name, Func<T> initialValue )
{
    if (_values.ContainsKey(name))
    {
        return (T)_values[name];
    }

    Set(name, initialValue());
    return Get<T>(name);
}

Then, to use it, you call:

Get<Map>(() => MyMap, () => new Map());

It gets you your behavior... it also makes it so the constructor of new Map() only gets called once.  A bit like Lazy<T>.

You like this?

 

Jun 18, 2010 at 4:53 PM

Yeah, I like the last version as well because the Map wouldn't be created until it's needed.  

Basically, I use the Get method even for Read Only properties (obviously without needing INotifyPropertyChanged) to maintain the same API for all my properties and also you get strongly typed names instead of magic strings (plus the added benefit of not having to create a field).  This is nice because it's like setting the initial value for a field but as you say, you get the benefit of lazy instantiation :)

Nice work on the ViewModelBase by the way.

Thanks,

Gene

Coordinator
Jun 18, 2010 at 5:51 PM

I will add it to the code base tonight.

Thanks,

Brian

Coordinator
Jun 19, 2010 at 2:49 AM

This feature has been added to the latest source code (not binaries)