Why should I avoid using Properties in C#?

后端 未结 14 525
渐次进展
渐次进展 2020-12-22 18:05

In his excellent book, CLR Via C#, Jeffrey Richter said that he doesn\'t like properties, and recommends not to use them. He gave some reason, but I don\'t really understand

相关标签:
14条回答
  • 2020-12-22 18:22

    Back in 2009, this advice merely seemed like bellyaching of the Who Moved My Cheese variety. Today, it's almost laughably obsolete.

    One very important point that many answers seem to tiptoe around but don't quite address head on is that these purported "dangers" of properties are an intentional part of the framework design!

    Yes, properties can:

    • Specify different access modifiers for the getter and setter. This is an advantage over fields. A common pattern is to have a public getter and a protected or internal setter, a very useful inheritance technique which isn't achievable by fields alone.

    • Throw an exception. To date, this remains one of the most effective methods of validation, especially when working with UI frameworks that involve data-binding concepts. It's much more difficult to ensure that an object remains in a valid state when working with fields.

    • Take a long time to execute. The valid comparison here is with methods, which take equally long - not fields. No basis is given for the statement "a method is preferred" other than one author's personal preference.

    • Return different values from its getter on subsequent executions. This almost seems like a joke in such close proximity to the point extolling the virtues of ref/out parameters with fields, whose value of a field after a ref/out call is pretty much guaranteed to be different from its previous value, and unpredictably so.

      If we're talking about the specific (and practically academic) case of single-threaded access with no afferent couplings, it's fairly well understood that it's just bad property design to have visible-state-changing side-effects, and maybe my memory is fading, but I just can't seem to recall any examples of folks using DateTime.Now and expecting the same value to come out every time. At least not any instances where they wouldn't have screwed it up just as badly with a hypothetical DateTime.Now().

    • Cause observable side effects - which is of course precisely the reason properties were invented as a language feature in the first place. Microsoft's own Property Design guidelines indicate that setter order shouldn't matter, as to do otherwise would imply temporal coupling. Certainly, you can't achieve temporal coupling with fields alone, but that's only because you can't cause any meaningful behaviour at all to happen with fields alone, until some method is executed.

      Property accessors can actually help prevent certain types of temporal coupling by forcing the object into a valid state before any action is taken - for example, if a class has a StartDate and an EndDate, then setting the EndDate before the StartDate could force the StartDate back as well. This is true even in multi-threaded or asynchronous environments, including the obvious example of an event-driven user interface.

    Other things that properties can do which fields can't include:

    • Lazy loading, one of the most effective ways of preventing initialization-order errors.
    • Change Notifications, which are pretty much the entire basis for the MVVM architecture.
    • Inheritance, for example defining an abstract Type or Name so derived classes can provide interesting but nevertheless constant metadata about themselves.
    • Interception, thanks to the above.
    • Indexers, which everyone who has ever had to work with COM interop and the inevitable spew of Item(i) calls will recognize as a wonderful thing.
    • Work with PropertyDescriptor which is essential for creating designers and for XAML frameworks in general.

    Richter is clearly a prolific author and knows a lot about the CLR and C#, but I have to say, it seems like when he originally wrote this advice (I'm not sure if it's in his more recent revisions - I sincerely hope not) that he just didn't want to let go of old habits and was having trouble accepting the conventions of C# (vs. C++, for example).

    What I mean by this is, his "properties considered harmful" argument essentially boils down to a single statement: Properties look like fields, but they might not act like fields. And the problem with the statement is, it isn't true, or at best it's highly misleading. Properties don't look like fields - at least, they aren't supposed to look like fields.

    There are two very strong coding conventions in C# with similar conventions shared by other CLR languages, and FXCop will scream at you if you don't follow them:

    1. Fields should always be private, never public.
    2. Fields should be declared in camelCase. Properties are PascalCase.

    Thus, there is no ambiguity over whether Foo.Bar = 42 is a property accessor or a field accessor. It's a property accessor and should be treated like any other method - it might be slow, it might throw an exception, etc. That's the nature of Abstraction - it's entirely up to the discretion of the declaring class how to react. Class designers should apply the principle of least surprise but callers should not assume anything about a property except that it does what it says on the tin. That's on purpose.

    The alternative to properties is getter/setter methods everywhere. That's the Java approach, and it's been controversial since the beginning. It's fine if that's your bag, but it's just not how we roll in the .NET camp. We try, at least within the confines of a statically-typed system, to avoid what Fowler calls Syntactic Noise. We don't want extra parentheses, extra get/set warts, or extra method signatures - not if we can avoid them without any loss of clarity.

    Say whatever you like, but foo.Bar.Baz = quux.Answers[42] is always going to be a lot easier to read than foo.getBar().setBaz(quux.getAnswers().getItem(42)). And when you're reading thousands of lines of this a day, it makes a difference.

    (And if your natural response to the above paragraph is to say, "sure it's hard to read, but it would be easier if you split it up in multiple lines", then I'm sorry to say that you have completely missed the point.)

    0 讨论(0)
  • 2020-12-22 18:23

    Personally I only use properties when creating simple get / set methods. I stray away from it when coming to complicated data structures.

    0 讨论(0)
  • 2020-12-22 18:24

    The argument assumes that properties are bad because they look like fields, but can do surprising actions. This assumption is invalidated by .NET programmers' expectactions:

    Properties don't look like fields. Fields look like properties.

    • A property method may throw an exception; field access never throws an exception.

    So, a field is like a property that is guaranteed to never throw an exception.

    • A property cannot be passed as an out or ref parameter to a method; a field can.

    So, a field is like a property, but it has additional capabilities: passing to a ref/out accepting methods.

    • A property method can take a long time to execute; field access always completes immediately. [...]

    So, a field is like a fast property.

    • If called multiple times in a row, a property method may return a different value each time; a field returns the same value each time. The System.DateTime class has a readonly Now property that returns the current date and time.

    So, a field is like a property that is guaranteed to return the same value unless the field was set to a different value.

    • A property method may cause observable side effects; field access never does.

    Again, a field is a property that is guaranteed to not do that.

    • A property method may require additional memory or return a reference to something that is not actually part of the object's state, so modifying the returned object has no effect on the original object; querying a field always returns a reference to an object that is guaranteed to be part of the original object's state. Working with a property that returns a copy can be very confusing to developers, and this characteristic is frequently not documented.

    This one may be surprising, but not because it's a property that does this. Rather, it's that barely anyone returns mutable copies in properties, so that 0.1% case is surprising.

    0 讨论(0)
  • 2020-12-22 18:24

    I haven't read the book, and you haven't quoted the part of it you don't understand, so I'll have to guess.

    Some people dislike properties because they can make your code do surprising things.

    If I type Foo.Bar, people reading it will normally expect that this is simply accessing a member field of the Foo class. It's a cheap, almost free, operation, and it's deterministic. I can call it over and over, and get the same result every time.

    Instead, with properties, it might actually be a function call. It might be an infinite loop. It might open a database connection. It might return different values every time I access it.

    It is a similar argument to why Linus hates C++. Your code can act surprising to the reader. He hates operator overloading: a + b doesn't necessarily mean simple addition. It may mean some hugely complicated operation, just like C# properties. It may have side effects. It may do anything.

    Honestly, I think this is a weak argument. Both languages are full of things like this. (Should we avoid operator overloading in C# as well? After all, the same argument can be used there)

    Properties allow abstraction. We can pretend that something is a regular field, and use it as if it was one, and not have to worry about what goes on behind the scenes.

    That's usually considered a good thing, but it obviously relies on the programmer writing meaningful abstractions. Your properties should behave like fields. They shouldn't have side effects, they shouldn't perform expensive or unsafe operations. We want to be able to think of them as fields.

    However, I have another reason to find them less than perfect. They can not be passed by reference to other functions.

    Fields can be passed as ref, allowing a called function to access it directly. Functions can be passed as delegates, allowing a called function to access it directly.

    Properties... can't.

    That sucks.

    But that doesn't mean properties are evil or shouldn't be used. For many purposes, they're great.

    0 讨论(0)
  • 2020-12-22 18:24

    I don't agree with Jeffrey Richter, but I can guess why he doesn't like properties (I haven't read his book).

    Even though, properties are just like methods (implementation-wise), as a user of a class, I expect that its properties behave "more or less" like a public field, e.g:

    • there's no time-consuming operation going on inside the property getter/setter
    • the property getter has no side effects (calling it multiple times, does not change the result)

    Unfortunately, I have seen properties which did not behave that way. But the problem are not the properties themselves, but the people who implemented them. So it just requires some education.

    0 讨论(0)
  • 2020-12-22 18:25

    It's just one person's opinion. I've read quite a few c# books and I've yet to see anyone else saying "don't use properties".

    I personally think properties are one of the best things about c#. They allow you to expose state via whatever mechanism you like. You can lazily instantiate the first time something is used and you can do validation on setting a value etc. When using and writing them, I just think of properties as setters and getters which a much nicer syntax.

    As for the caveats with properties, there are a couple. One is probably a misuse of properties, the other can be subtle.

    Firstly, properties are types of methods. It can be surprising if you place complicated logic in a property because most users of a class will expect the property to be fairly lightweight.

    E.g.

    public class WorkerUsingMethod
    {
       // Explicitly obvious that calculation is being done here
       public int CalculateResult()
       { 
          return ExpensiveLongRunningCalculation();
       }
    }
    
    public class WorkerUsingProperty
    {
       // Not at all obvious.  Looks like it may just be returning a cached result.
       public int Result
       {
           get { return ExpensiveLongRunningCalculation(); }
       }
    }
    

    I find that using methods for these cases helps to make a distinction.

    Secondly, and more importantly, properties can have side-effects if you evaluate them while debugging.

    Say you have some property like this:

    public int Result 
    { 
       get 
       { 
           m_numberQueries++; 
           return m_result; 
       } 
    }
    

    Now suppose you have an exception that occurs when too many queries are made. Guess what happens when you start debugging and rollover the property in the debugger? Bad things. Avoid doing this! Looking at the property changes the state of the program.

    These are the only caveats I have. I think the benefits of properties far outweigh the problems.

    0 讨论(0)
提交回复
热议问题