1

Lately I have come across quite a few scenarios where an open and a read only version of a class is required. A common scenario is a settings class that users can set properties for but when the settings have been validated and going through a long-running operation, they should only have access to a read-only version.

These classes are not a generic store and are strongly typed.

Currently I just inherit from a read/write version and throw exceptions on write attempts and was wondering if there is a more streamlined way people do this.

Raheel Khan
  • 14,205
  • 13
  • 80
  • 168
  • You could follow the `ICollection` methodology of just providing a `IsReadOnly` property. https://msdn.microsoft.com/en-us/library/92t2ye13%28v=vs.110%29.aspx – itsme86 Feb 13 '15 at 17:49
  • Why not just have the set return without setting the value once it is validated? – paparazzo Feb 13 '15 at 17:55
  • You don't need to make your *properties* read-only to stop your *users* from setting them unless there is something fundamentally wrong with the structure of your application. What you need is validation logic. Or by *users* do you mean *developers*? – Ant P Feb 13 '15 at 17:58

5 Answers5

1

Why not use an interface? Pass the object as the interface where you want it to be read only but pass it as a concrete type where you want it to be read-writable.

    public interface IUserSettings
    {
        int Value1 { get; }
        string Value2 { get; }
    }

    public class UserSettings : IUserSettings
    {
        public int Value1 { get; set; }
        public string Value2 { get; set; }
    }

You could also then update your UI to display UserSettings and IUserSettings differently (i.e. have 1 template show edit controls and 1 template show read only controls.)

dbudzins
  • 436
  • 5
  • 8
  • +1. That's what Microsoft's Common Compiler Infrastructure (CCI) library does (IIRC). And what you can do with collections and IEnumerable<>. However there's one drawback: whoever sees the object via the interface type only gets a read-only version, but not an immutable one: the object's value/contents can still be changed by whomever has a class-typed reference to the same object. If you can pair up this solution with cloneable types, you'd have an easy way around this issue. – stakx - no longer contributing Feb 13 '15 at 18:08
1

First, note that there is a difference between "read-only" and "immutable". Let's say you giving r ("receiver") a reference to your object o ("object"):

  • If you just want to be sure that r won't change the value of o, then an interface-based solution like this one should suffice and is probably as easy as it will get.

    var o = new List<int> { 1, 2, 3 };
    r.LookAtList((IEnumerable<int>)o);
    

    r.LookAtList will see o as a read-only sequence because the IEnumerable<> abstraction is read-only.

  • If you also want to ensure that r will always observe the same value for o, then that interface-based solution won't be enough. Consider this:

    var o = new List<int> { 1, 2, 3 };
    r.LookAtList((IEnumerable<int>)o);
    o.Add(4);
    r.LookAtList((IEnumerable<int>)o);
    

    While r.LookAtList won't be able to change the original object o (unless it uses reflection or casts the IEnumerable<> back to a List<>), it will observe a different sequence the second time around, even though it is passed exactly the same IEnumerable<> reference.

If you truly want a read-write version of some type and an immutable version, then you will end up with two distinct types. I suggest that you consider the Builder pattern:

sealed class FooBuilder
{
    public TA A { get; set; }
    public TB B { get; set; }
    …
    public Foo Build() { return new Foo(A, B, …); }
}

sealed class Foo
{
    public Foo(TA a, TB b, …)
    {
        … // validate arguments
        this.a = a;
        this.b = b;
        …
    }

    private readonly TA a;
    private readonly TB b;
    …

    public TA A { get { return a; } }
    public TB B { get { return b; } }
    …
}

But that is quite verbose and you probably hoped to get around all that duplication. Unfortunately, implementing truly immutable types requires a lot of verbosity in the current version of the C# programming language.

This might change somewhat in the upcoming version (6) of C#.

Community
  • 1
  • 1
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
0

My first notion would be to have a struct FooDefaults with properties for the default values, and pass that into the constructor for Foo (your property class), which uses the defaults (after validation) to initialize members returned from readonly properties.

Brian Gradin
  • 2,165
  • 1
  • 21
  • 42
0

The pattern used by Microsoft's own Freezable hierarchy in WPF does exactly what you describe. See for instance Freezable.WritePreamble:

        if (IsFrozenInternal)
        {
            throw new InvalidOperationException(
                SR.Get(SRID.Freezable_CantBeFrozen,GetType().FullName));
        }

Freezable uses 'IsFrozen` for clients to figure out whether an object is immutable or not.

NextInLine
  • 2,126
  • 14
  • 23
  • Speaking of "freezing", see this related question: http://stackoverflow.com/questions/13691612/how-to-freeze-a-popsicle-in-net-make-a-class-immutable. Eric Lippert has a series of blog articles about immutability, starting with [this article](http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx "Eric Lippert: Fabulous Adventures in Coding: Immutability in C# Part One"). – stakx - no longer contributing Feb 13 '15 at 18:16
0

why not just shut down the set?

private string prop1 = string.Empty;
public string Prop1 
{
    get { return prop1; }
    set 
    {
        if (ValueIsValid(prop1))
        {
            NotifyPropertyChanged("Prop1");
            return;   // or you can throw an exeption 
        }
        if (prop1 == value)  return;
        prop1 = value;
        NotifyPropertyChanged("Prop1");
    }
}
paparazzo
  • 44,497
  • 23
  • 105
  • 176