plukow
plukow

Reputation: 31

Make list/collection of objects with public properties truly read-only

I have following problem.

Let's say there is a public Class1 defined like this:

public class Class1
{
     public string pr1 { get; set; }
     public string pr2 { get; set; }

     public Class1()
     {
     }
}

I'm not allowed to modify class definition - it used by other people and they need it written this way. And here comes trouble.

I need static List<Class1> myStaticList which can be modified only inside Class2 and everywhere else is truly read-only. And by 'truly read-only' I mean user mustn't change anything including properties of list's elements.

Something like: Class2.list[0].pr1="newValue" called from outside of Class2 should be blocked.

ReadOnlyCollection don't do a job because user can still modified given element of a list, so solution given here List<T> readonly with a private set doesn't satisfy my needs.

Basically I've found a lot of possible ways of making list/collection read-only (e.g. IReadOnlyList) but all of them left possibility of changing properties of given list element if they have public setter.

The only solution I've found is creating new class:

public class Class1ReadOnly : Class1
{
    public new string pr1{ get; private set; }
    public new string pr2{ get; private set; }
    public Class1ReadOnly(Class1 tmp)
    {
        pr1= tmp.pr1;
        pr2 = tmp.pr2;
    }
}

But I hoped two find better way since it seems to me kind of strange to define whole new class just because of one single read-only list

Thanks in advance for any clues.

Is this even possible to this different way?


Real-world context is like this:

In code we edit database and I want to have backup list of records which has been changed with their initial values. I want to have it static just to have possibility of restoring DB everywhere I want. Class1 contains all the values of single record of our DB so by List<Class1> I can keep all modified records and restore them if it is needed. And since it is backup it is essential to be read-only because unintended change can lead to the disaster.

Upvotes: 3

Views: 632

Answers (3)

jdphenix
jdphenix

Reputation: 15415

Given the circumstance you have your approach is one that works. You're using a solution that is the adapter pattern. It is not strange to do something like this.

Adapter Pattern
Intent
Convert the interface of a class into another interface clients expect.

- Design Patterns (ISBN 0-201-63361-2)

I would define such a class like this:

public interface IClass1 {
  string pr1 { get; }
  string pr2 { get; }
}

internal class Class1Adapter : IClass1 {
  public static Class1Adapter FromClass1(Class1 class1) {
    var pr1 = class1.pr1;
    var pr2 = class1.pr2;
    return new Class1Adapter {pr1 = pr1, pr2 = pr2};
  }

  public string pr1 { get; private set; }
  public string pr2 { get; private set; }
}

Why make the choices I did?

  • internal class so I don't leak out to other assemblies. That way other teams can't use Class1Adapter unless you explicitly let it out.
  • Implement a new interface IClass1. You say you are not able to change the definition of Class1, but if it wasn't an API breaking change... you might be able to pitch it to the other team. You could with the current definition change Class1 to implement IClass1 and with no other changes, you could in your code:
var list = new ReadOnlyCollection<IClass1>(source); 

// ... and later, if attempted

list[0].pr1 = "I'm breaking you!"; 

Would produce:

Property or indexer 'ConsoleApplication19.IClass1.pr1' cannot be assigned to -- it is read only

Upvotes: 2

Peter Duniho
Peter Duniho

Reputation: 70652

By "I'm not allowed to change it", do you mean that (as the question wording implies) you are not allowed to modify the values in the class? Or that you literally are not allowed to modify the class declaration itself? Also, if you are not allowed to modify the class declaration, what about the code that uses the class in collections? E.g. would a List<Class1> object be required to continue to be a List<Class1>, or could you replace the Class1 type with something else?


Without more context, it is difficult to know for sure what is best in your case. However, one possible approach is for Class1 to be replaced by an interface:

interface IClass1
{
    string str1 { get; }
    string str2 { get; }
}

Then declare Class1 as implementing that interface, nested inside Class2:

class Class2
{
    private class Class1 : IClass1
    {
        public string str1 { get; set; }
        public string str2 { get; set; }
    }
}

Now, using the techniques you've already seen for making the list itself read-only, you prevent any other code from replacing elements within the list (i.e. with some other implementation of the interface). And only Class2 can successfully cast the implementation of the IClass1 element in the list to Class1 to gain access to the public setter for the properties.

If that doesn't address your concern, it would be helpful if you could be more specific about exactly what constraints exist for solving the problem.

Upvotes: 2

Petar Minev
Petar Minev

Reputation: 508

How about Protected set?

https://msdn.microsoft.com/en-us/library/bcd5672a.aspx?f=255&MSPPError=-2147217396

or you can override property setter.

:)

Upvotes: 1

Related Questions