简体   繁体   中英

What are better alternatives to getters and setters in C#?

I have read some about the use of getters/setters in object oriented programming and many resources such as this suggest that they should be used very infrequently. A quote from that article says:

Don't ask for the information you need to do the work; ask the object that has the information to do the work for you.

While that sounds good in principle, I'm not particularly sure how that is best implemented. Say taht I have a class the describes a car. Inside that class, lets say that the brand of this car stored in the class is "Toyota". Now I have a GUI that wants to display that this is a "Toyota". How does one do that without a getter?

Even if I abstract out that the "Toyota" is stored as a String and return a TextBox with "Toyota" already written in it, this sounds, at best, like a getter of a wrapped form of the original property. I just thought that things like TextBoxes should be only in the GUI's class, and not in helper classes like my car class here.

That article is describing that you should use methods instead of property getters/setters. Basically it's suggesting a method should perform an action to modify state instead of allowing a setter to modify state. eg I could have a class with a Month and a Day property. I could then have code that does this:

obj.Day = 28;
obj.Month = Months.February;
obj.Day = 30;
obj.Month = Months.March;

As soon as I set a day to 30 while month is February, I have invalid state.

The article suggest that a class shouldn't allow this type of thing but provide specific methods to perform specific actions, like:

obj.ChangeDate(Months.March, 30);

If a date time analogy is confusing due to DateTime, you could have a similar example with lat/long coords:

obj.Latitude = 45.4112;
obj.Longitude = -75.6981;
//... 
obj.Latitude = 39.73;
obj.Longitude = -86.27;

When Latitude is set to 39.73 the location becomes a place near Newark instead of Ottawa (the first lat/long) or Indianapolis (the second lat/long). Leaving settings for lat/long leaves the interface open to not being able to verify its invariants and some may say not truly object-oriented. So, you might have a method instead:

obj.MoveTo(39.73, -86.27);

OO implies both encapsulated state and behaviour. While some may thing that properties "encapsulate" state, they normally don't. They decouple implementation from the interface so that what is used as a backing store for the properties can change--but, it rarely ever does; so the properties are simply making available implementation details differently than providing public fields.

There are concepts like Command Query separation that build on this idea that suggest an interface should either update state or query state, but not both. Properties with both getters and setters provide an interface that both update state and query state. The above examples MoveTo and ChangeDate would be "commands". Conceptually you would provide other ways of querying state; if that's needed.

One of the other problems with properties is that people habitually add both getters and setters simply because it is a property. The ability to get or set a property may not be necessary and simply adding both a getter and a setter by rote creates and interface that requires testing and maintenance that may not ever be needed.

Subtle? Sure; but he's talking about the object-oriented design, not code that "just works".

You're ignoring "to do the work". If the work you want done is the brand of the car, then a getter that returns the brand does the work.

What the article is saying is if you want work done on class data, put the work in the class. Do not pull data out of the class, do work on it, then return it to the class:

var c = new Car();
c.Odometer += 10;  // bad
c.DriveMiles(10);  // good

I also question how applicable that article is to C#; it claims:

Getter and setter methods (also known as accessors) are dangerous for the same reason that public fields are dangerous: They provide external access to implementation details.

In C# this is false, a C# accessor can accept arbitrary classes, they do not "provide external access to implementation details".

Getters and setters are not evil, they just shouldn't be generated automaticaly for every attribute. As far as your example is concerned, a Car class should probably have a getBrand method returning a String, but a setter for this attribute is probably not needed. A method in Car returning a TextBox with the brand name is most probably worse idea than a simple getter returning a String, because it's not very generic.

I can see Peter Ritchie's answer now: good example with the changeDate. The Date class should provide a method like that rather than providing a getter for day and month and requiring user code to compute next day in the calendar on it's own.

Basically what he's saying is, say you had a class with ten properties, and you wanted to dump it to a line of csv. Add a Tocsv method to the class to return a string with a pile of csvs in it. Don't write an other class that accesses it through properties and builds the csv string as by necessity they become very tightly coupled.

Not sure why he singled out accessors, the proplem would be exactly they same with discrete methods to get and set, or worse still member variables.

Obviously there are limits, you'd have to have really good reason for replacing

SomeLabel.Text = SomeInstance.StringValue;

with

SomeInstance.MakeStringValueALabel(SomeLabel);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM