views:

257

answers:

4

I have read in many places that exposing fields publicly is not a good idea, because if you later want to change to properties, you will have to recompile all the code which uses your class.

However, in the case of immutable classes, I don't see why you would ever need to change to properties - you're not going to be adding logic to the 'set' after all.

Any thoughts on this, am I missing something?

Example of the difference, for those who read code more easily than text :)

//Immutable Tuple using public readonly fields
public class Tuple<T1,T2>
{
     public readonly T1 Item1;
     public readonly T2 Item2;
     public Tuple(T1 item1, T2 item2)
     {
         Item1 = item1;
         Item2 = item2;
     }
}

//Immutable Tuple using public properties and private readonly fields
public class Tuple<T1,T2>
{
     private readonly T1 _Item1;
     private readonly T2 _Item2;
     public Tuple(T1 item1, T2 item2)
     {
         _Item1 = item1;
         _Item2 = item2;
     }
     public T1 Item1 { get { return _Item1; } }
     public T2 Item2 { get { return _Item2; } } 
}

Of course, you could use auto-properties (public T1 Item1 { get; private set; }), but this only gets you 'agreed immutability' as opposed to 'guaranteed immutability'...

A: 

As a standard practice I follow your 2nd example only using 'readonly' when the object is public or vulnerable to inadvertent tampering. I am using the 'agreed immutability' model in a current project building a plugin framework. Obviously, with agreed immutability, the readonly protection is removed.

Only in rare circumstances do I expose a field - public, internal, or otherwise. It just doesn't feel right unless writing a property {get;} takes more time than I'm willing to give.

dboarman
+2  A: 

It is an obvious omission from properties that you cannot write something like:

     public T2 Item2 { get; readonly set; } 

I'm not even sure readonly is the best word to use to mean "can only be set in the constructor", but that's what we're stuck with.

This is actually a feature that many people have requested, so let's hope that it will be introduced in a hypothetical new version of C# some time soon.

See this related question.

Mark Byers
Hm, a "hypothetical new version of C#" I think I know who's blog you've been reading ;)
Benjol
Yep, we're considering it. Hypothetically. It's actually a rather more difficult problem than it appears at first glance.
Eric Lippert
@Eric Lippert, now you're just teasing us. I expect a post about the difficulties sometime soon :)
Benjol
@Benjol: the tricky bit is that it would be highly desirable to make initialization of such a property look like the initialization of an anonymous type, or like an object initializer. But in the former, we generate the constructor, and in the latter, the property is changed after construction. How to rationalize all that is non-obvious.
Eric Lippert
@Eric, no way to fit the Naj in there somewhere? :)
Benjol
I'm marking this one as the answer, even thought it doesn't answer my question directly ...
Benjol
A: 

The idea behind properties is that, even if you don't intend to chang them now or later, mabye you might need to in some unforseen way. Let's say you need to change the getter to do some kind of calculation, or logging. Maybe you need to add exception handling. Lots of potential reasons.

Also consider semantics. if T1 is a value type rather than a reference type, then accessing obj.Item1 returns a copy of _Item1 in the getter, while accessing Item1 without a getter would not retrieve a copy. This means that while Item1 may be immutable internally, the returned value type object isn't. I can't think of a reason why that would be a good thing, but it is a difference.

Mystere Man
What's that about copying when you return via a property? Do you have a reference for that?
Benjol
Value types have to be copied when you pass them. It's how C# works.
Mystere Man
Yeah, but why wouldn't the field also be copied on passing? I can't see where this would be a problem.
Benjol
if you're passing it. obj.field.ToString() works on the original instance. obj.property.ToString() works on a copy if it's a value type.
Mystere Man
+1  A: 

You may not need to add any logic to a setter in the future, but you may need to add logic to a getter.

That's a good-enough reason enough for me to use properties rather than exposing fields.

If I'm feeling rigorous then I'd go for full immutability (explicit readonly backing fields with exposed getters and no setters). If I'm feeling lazy then I'd probably go for "agreed immutability" (auto-properties with exposed getters and private setters).

LukeH