Encapsulating how a private member variable is modified, but allowing the member to be accessible for reading

StackOverflow https://stackoverflow.com/questions/23302931

  •  09-07-2023
  •  | 
  •  

Question

This is a very basic OOP question. I don't want to do a copy of _objectSettings and feel like I'm having a brain fart. The question can be seen in the Getter function in the object.

Example:

public class CoolObject
{
    private Dictionary<long, bool> _objectSettings;
    public CoolObject(){
         //.. do stuff//
    }
    public void UpdateSettings(long, bool){
         // enforce new setting obey's some rules
    }
    public Dictionary<long,bool> GetSettings(){
         // return Dictionary of settings, but not allow users to modify _objectSettings
    }
}

thanks :)

Edit: I'm on .Net 4.0, so the answer I select might not reflect the most optimal solution for future readers.

Was it helpful?

Solution

Either make a copy or wrap your dictionary in ReadOnlyDictionary, .NET 4.5 required, but you can easily implement your own if you are not on .NET 4.5.

public Dictionary<Int64, Boolean> GetSettings()
{
   // Return a snapshot of the current settings.
   return new Dictionary<Int64, Boolean>(this._objectSettings);
}

public ReadOnlyDictionary<Int64, Boolean> GetSettings()
{
   // Return a read-only wrapper around the current settings.
   return new ReadOnlyDictionary<Int64, Boolean>(this._objectSettings);
}

All callers will see modifications to the settings with the later options, while modifications made to the settings after obtaining the snapshot will not be visible with the former option.

If you want protection against unintentional modifications in your own code base both options mentioned are okay but you could also get away with a weaker form of protection by just making the return type IReadOnlyDictionary<TKey, TValue> or IEnumerable<KeyValuePair<TKey, TValue>>. The caller could just cast back to Dictionary<TKey, TValue> and make modifications but this is not a big issue inside your own code base.

public IReadOnlyDictionary<Int64, Boolean> GetSettings()
{
   // Just return the dictionary with property type IReadOnlyDictionary`2 but
   // then evil callers can still do the following.
   // ((Dictionary<Int64, Boolean>)coolObject.GetSettings()).Clear();
   return this._objectSettings;
}

If you expose the object to third party code, for example potentially malicious plug-ins, you really want to avoid this. Further you will have to revoke reflection permission because otherwise third party code could still just get hold onto you private dictionary or unwrap the read-only wrapper and modify it.

OTHER TIPS

Try returning an IReadOnlyDictionary.

public IReadOnlyDictionary<long,bool> GetSettings(){
    return _objectSettings;
}

That interface is implemented by Dictionary and does not allow for changes to the Dictionary.

[SerializableAttribute]
[ComVisibleAttribute(false)]
public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, 
    ICollection<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, 
    IReadOnlyDictionary<TKey, TValue>, IReadOnlyCollection<KeyValuePair<TKey, TValue>>, 
    IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable, ISerializable, 
    IDeserializationCallback

You can make the class immutable by making the setter private and injecting the value via constructor.

public class CoolObject
{
    public CoolObject(ImmutableSortedDictionary<long, bool> objectSettings){
         ObjectSettings = objectSettings;
         //.. do stuff//
    }

    public ImmutableSortedDictionary<long,bool> ObjectSettings
    {
        get
        {
            // return Dictionary of settings, but not allow users to modify _objectSettings
        }

        private set
        {
            // enforce new setting obey's some rules
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top