tags:

views:

730

answers:

13

I know how to use properties and I understand that they implicitly call underlying get and set accessors, depending on whether we are writing to or reading from a property.

static void Main(string[] args)
{
    A a = new A();
    (a.b).i = 100;

}

class A 
{
    private B _b = new B();
    public B b
    {
        get { return _b; }
    }
}
class B  
{
    public int i;
}

What code (a.b).i = 100; essentially does is that first property’s get accessor returns a reference to an object _b, and once we have this reference, we are able to access _b’s members and change their values.

Thus, in our example, having read only property only prevents outside code from changing the value of a reference variable _b, but it doesn’t prevent outside code from accessing _b’s members.

So it seems that property can only detect whether we are trying to read from or write to a variable ( in our case variable _b ) located on the stack, while it’s not able to detect whether we’re trying to also write to members of an object to which the variable on the stack ( assuming this variable is of reference type ) points to.

a) But doesn’t that defeat the whole purpose of having read-only properties? Wouldn’t it be more effective if properties had the ability to also detect whether we’re trying to access members of an object returned by get accessor( assuming backing field is of a reference type )?

thank you

+26  A: 

Immutability is not transitive; you can't expect mutable objects into an immutable accessor to be immutable.

plinth
This is one of the things I would like to see in the future; transitive immutability. The D programming language already has this, and it's a nice way of protecting against errors and simplifies concurrency a great deal.
simendsjo
@simendsjo: C++ also has it :) Unfortunately, `const`-correctness is very annoying and too-often disregarded, which is why it was not included in C# or Java
BlueRaja - Danny Pflughoeft
I like this answer, but the question implies a lack of understanding of mutability, so I'm not sure how useful it is.
Greg
+5  A: 

Of course you can access B.i; it's public. You're thinking that since _b is private, all methods should be private when fetched through A? In that case it's pretty useless as you wouldn't be able to use B for anything.

simendsjo
+19  A: 

Your reference is read only, not your object.

Andrew Lewis
Simple and concise. +1
musicfreak
+9  A: 
  1. No, it does not defeat the purpose of read-only properties.
  2. It is possible to use read-only properties that don't let the user change the underlying data. For example, you can have your property return a System.Collections.ObjectModel.ReadOnlyCollection even though the underlying type is a List. This, of course, won't prevent the user from changing the properties of the items in the collection.
Brian
And in general, you always have the option of creating a read-only interface/wrapper for the B type, and making only that accessible through the read-only property -- this is the pattern that ReadOnlyCollection is a (sadly underused) instance of.
Andy Mortimer
+2  A: 

As a minor point, you don't have to write (a.b).i = 100; but simply

a.b.i = 100;

Back to your question, I don't think it defeats the purpose. You can still not do the following:

a.b = new B();

because there's no public set(). If you want the member i of class B to be read only, you can do the same thing as you did to member _b of class A by making it private and providing a public get(), but not set(). Off the top my head, doing what you propose might lead to many unexpected consistencies (I'm sure the language designers did not overlook this).

Khnle
But you can still do: /// innocent function void IDontDoAnything(A a) { a.B.DestroyEverything(); }
simendsjo
+2  A: 

Entirely dependent on the situation, but read only access to a mutable object is a commonly used design. In many cases you simply want to ensure that the object itself remains the same.

Some classes (like String object in Java, and I believe in C# as well) are entirely immutable, where as others are only partially mutable. Consider an ActiveRecord style of object for which most fields are mutable, but the ID is immutable. If your class holds an ActiveRecord in a read only property, external classes cannot swap it for a different ActiveRecord object and thus change the ID, which might break assumptions within your class.

Chadwick
+12  A: 

Imagine a class like this:

public class A
{
    private List<int> _myList<int> = new List<int>();
    public List<int> MyList { get { return _myList; } }
}

Now, users of the class can add and remove and access items in the list, but they cannot replace the list itself. This is important. It allows you to do things inside the class like assume the _myList member is never null, for example.

Put a more general way, this paradigm allows you do define an interface into your class such that users can use the types in the properties you expose, but they cannot just swap instances of complex types out from under you.

Joel Coehoorn
thank you all for your help
flockofcode
+2  A: 

I disagree. Your property is for the class B, not for the members of class B. This means you can't assign a new Object to b. It doesn't mean that B's public members suddenly become private.

P.Brian.Mackey
+5  A: 

You ask:

Doesn’t that defeat the whole purpose of having read-only properties?

But look: your B.i member is a public field.

I ask you, then: what is the purpose of having a public field? It only makes sense if you want users of your code to be able to change that field's value. If you don't want that, it should be a private field, or (if you want to provide read but not write access) a property with a private set accessor.

So there's your answer. private B _b serves its purpose in the code you posted quite well (_b cannot be externally set to something new), just as public int i serves its purpose equally well (i can be externally changed).

Dan Tao
+4  A: 

Reference immutability is a popular feature request. Too bad its is so dramatically non CLS compliant. Very few languages have this notion, I only know of C++ (but don't get out much).

The key problem that this needs to be enforced by the CLR. C++ doesn't need to enforce this at runtime, only a C++ compiler is required to ensure that const contracts are observed. It has no support at all for language interop, beyond a bolt-on like COM.

This won't fly in .NET, there's little point in declaring a reference immutable and have that verified by the compiler when another language can stomp all over it because it doesn't have the syntax to express immutability. I reckon we'll get it some day, not Real Soon.

Hans Passant
+2  A: 

readonly applies to the class property, not the object that the property refers to. It keeps you from being able to write a.b = new B();, and that is all it does. It places no constraints on what you can do to the object once you get a reference to it. I think what you are discovering is that readonly make the most sense when applied to value types or immutable class types.

Brian
+2  A: 

Another use case:

interface INamedPerson
{
    String Name { get; }
}

class Bob : INamedPerson
{
    public String Name { get; set; }
}

class Office
{
    // initialisation code....

    public INamedPerson TheBoss { get; }

    public IEnumerable<INamedPerson> Minions { get; }
}

Now, if you have an instance of the Office, as long as you don't go cheating with casts, you have read-only access to everyone's names, but can't change any of them.

Douglas
+1  A: 

Ah. Encapsulation does the instantiated class inherit the containing class's access level. Exposing type B as a public property of type A. 'B.i' is public so it should be accessible from outside the same way 'A.b' is public.

A.b returns a reference of a privately accessible type B, however type B has a publicly accessible field i. My understanding is that you can set the i field of B but you can't set the b property of A externally. The B type property of A is readonly however the reference to type B does not define the same readonly access to its fields.

I'm sure you can modify the definition of type B to suit your need for the access level of B's fields or properties.

pymendoza