Tuesday 8 March 2005 — This is almost 20 years old. Be careful.
I’ve been using FxCop to lint my C# code. It’s a very impressive tool. It uses introspection to analyze .NET assemblies and report on all sorts of problems. Like most tools of this sort, some of the suggestions are useful, some are just noise, and some seem contrary to progress.
One of the things it’s bothered about is public members in classes. If you write code like this:
// version 1
class MyClass
{
public string MyString;
}
then FxCop will complain and direct you to this explanation: Do not declare visible instance fields. The guidance directs you to rewrite it like this:
// version 2
class MyClass
{
private string myString;
public string MyString
{
get { return myString; }
set { myString = value; }
}
}
The reason given for all the extra verbiage is to avoid exposing implementation details. But this seems misguided to me. After all, the caller of version 1 writes something like this:
MyClass myobj = new MyClass();
myobj.MyString = "little bunny foo-foo";
while the caller of version 2 writes something like this:
MyClass myobj = new MyClass();
myobj.MyString = "little bunny foo-foo";
That’s right: they’re exactly the same. So how have I exposed an implementation detail? I could start with version 1, and change it to version 2, and no calling code would have to change at all. Ergo, the implementation is not exposed.
The FxCop documentation takes pains to point out that version 2 is optimized to be as efficient as version 1. But I still have to write and later wade through those extra lines of code, and for what?
Am I missing something because I’m a C# and .NET newbie? In Python, getters and setters are superfluous. Is C# so different?
Comments
A lot of this seems like B&D programming, where I feel a lot more comfortable with the dynamic type system in Python.
From what I can tell, the only reason to do this is that field accesses can't be virtual. So any inheriting class that wanted to catch gets or sets of a member of the parent class can only do it if the parent class used properties.
Otherwise, the compiler will force them to shadow the parent's field with the new modifier, and any access to the member through a parent-typed reference to a child instance would resolve to the statically determined parent field, rather than the child property.
Even if that's a justification, I've never seen people who make these do-nothing properties make them virtual. Which makes the effort entirely pointless.
The only time I can see that I would even think about doing this is where I was building a library/framework where programmers subclassing the framework classes wouldn't be able to change the library to convert a parent class's field to a virtual property in the event that they needed to catch a field access in a child class. And even then I'd be more likely to curse the .NET developers for not allowing fields to be virtual (and not making things virtual by default).
<pant pant> Sorry for the rant.
MyClass.set_MyString("little bunny foo-foo");
private MyEnum myStuff;
public string MyString
{
get { return myStuff.ToString() ; }
set { myStuff = Enum.Parse(typeof(MyString), value); }
}
And the caller can still use
thingy.MyString = "Thing1";
(The difference being, that now, the setter will have the possible side effect of throwing an exception if the string you set the property to does not represent an item in the enum...)
So, the primary benefit is the flexibility to go back and change the implementation later and not adversely impact consuming code. And if you're worried about all that typing, I know of a good code generator... :)
Properties also allow you to add side effects to the getters and setters as you go along. For example:
public string Foo
{
get {this.requests++; return myFoo; }
set {this.changes++; myFoo = value; }
}
Also, in subclasses, you can do funky stuff like
public override string Bar
{
get { return base.Bar + ", and much, much more!!!"; }
}
So yes - on trivial classes, it does look ridiculous. But as you start to write production code, it does save you ass big style.
I banged on a lot about this in my lotusfear presentation..
http://www.billbuchan.com/web.nsf/htdocs/BBUN693HVL.htm
---* Bill
The popularity of Java is to blame here. Java programmers that are used to the getter/setter idiom take this baggage with them into other languages. In my opinion, except for inheritance and binary compatibility, it's wasteful to make properties that directly get and set a member variable. I think the language designers could make a few tweaks to make this completely transparent and greatly reduce code clutter.
See http://www.geocities.com/csharpfaq/properties.html
I personally don't mind having public members for the same reason that you argued. "ref" is not very common anyways...
What I'm complaining about is the blanket assertion that public fields are bad in C#, when there's a perfectly good non-interface-breaking migration path from public fields to properties.
(Admittedly, the binary interface would change, as Kevin points out. I've never seen this to be a problem, but I suppose in a very large project where a recompile would take significant time it could be an issue. I would tend not to worry about this time - it's only a computer. They like doing tedious repetitive things!)
Certainly, if you want to be able to lazily initialise members, or log accesses, or make them derive from other members, or any other trickery, properties are great. But if you're not doing anything with them, they are busywork. And that makes me sad.
Related, the form builders will show you properties on your control/component, but not fields.
If public fields are so wrong, why do you have to go to the trouble of typing an extra keyword every time to control theirt visibility? I miss the C++ convention, where a whole slew of declarations were marked private or public in one go.
So much simpler in SmallTalk, where methods are always public and member variables always private: no need for special keywords at all.
aList.DataSource = myClass;
aList.DisplayMember = "aMemberProperty";
aList.ValueMember = "anotherMemberProperty";
For string seems to work with public member (instead of property), but for int doesn't. This is at least one reason to use properties instead of public members.
Add a comment: