7

Consider these two ways to declare a field in a C# class

option A

public class AuditController
{
    public DataAccess Service;
}

option B

public class AuditController
{
    private DataAccess service;
    public DataAccess Service { get => service; set => service = value; }
}

Which one is better? My code analyzers (sonarqube, resharper, etc) unanimously say B is better. But I disagree. I can see that B encapsulates the field, so that a person can later add validation and logging, etc. However, why not wait until you need it?

This is the YAGNI principle: you can add the getter/setter now, or wait until you need it. Since there is no penalty for waiting, you should wait. Because most likely... you ain't gonna need it.

Am I missing something?

I'm not asking whether getter/setters provide any benefit of encapsulation. I'm asking whether it is best practice to proactively add getter/setters that are doing nothing other than exposing the data.

  • 2
    Possible duplicate of When are Getters and Setters Justified – gnat Aug 24 '18 at 16:29
  • @gnat, thanks for the link. I think that it is a different question. I'm not asking whether getter/setter provide any benefit of encapsulation. I'm asking whether it is best practice to proactively add getter/setters that are doing nothing other than exposing the data. – John Henckel Aug 24 '18 at 16:33
  • We can't answer which is better unless we know how the DataAccess service is used in the bigger picture. – whatsisname Aug 24 '18 at 16:44
  • 7
    stackoverflow.com/q/737290. If you ever decide you need to switch to a public property instead of a public field, it's a binary breaking change. – Robert Harvey Aug 24 '18 at 16:57
  • The main reason for using properties is to satisfy the .net framework design guidelines, and the code analysis engine. It does give you a tiny bit of freedom to store the value elsewhere in the future. – Frank Hileman Aug 24 '18 at 23:56
16

There are two completely different kinds of software: libraries that need a stable binary interface across multiple versions, and applications or internal software where you can just refactor.

For internal software or applications, you are right: you can wait until something is needed, then refactor and recompile your code. So using public fields is jucky but fine.

A public[1] library doesn't have this liberty. If you change a field to a property that is a breaking change – a property is basically a method with prettier syntax. This change is source-compatible (stable API) but not binary-compatible (unstable ABI). Any dependent code that accesses this field/property has to be recompiled.

[1]: Here, “public” means “consumed by developers outside of your team”.

A lot of design advice that you see (SOLID, design patterns, …) is focused on keeping your software evolvable while still keeping ABI compatibility. This isn't bad advice, it's just not always applicable. YAGNI is the complete opposite because it assumes that the design can be fixed by refactoring. Again not bad advice, just not always applicable either.

This is not a free pass for ignoring any design advice, just a pointer that design advice tends to assume a particular context.

But even ignoring the necessity of using properties, you should always use them:

  • Properties are idiomatic C# code. Not using them makes your code more complicated to read.
  • They are not that more code to type. Deal with it.
  • Public fields in themselves are a design smell, and reek of insufficient object modeling for anything more complicated than a DTO. Properties can be publicly gettable and privately settable, which is often the better choice.
  • All databinding technologies (ASP.Net MVC Model Binding, WinForms, WFP, Xamarin, Asp.Net WebAPI, …) will only databind to properties.
  • Some cases need properties, so you might as well use them anywhere. The downsides – a tiny bit more typing, in rare cases a tiny performance hit – usually don't outweigh the increased consistency.
  • 1
    This is a very good answer, +1. But AFAIK you are wrong about the performance hit - properties which are mapped to private fields in the shown trivial manner are optimized away by the C# compiler internally. – Doc Brown Aug 24 '18 at 18:25
  • 1
    Thanks for the distinction between API and ABI. I didn't know that! In that case there is a cost to waiting, so I should proactively add getter/setters. – John Henckel Aug 24 '18 at 20:06
  • @DocBrown in fact, all auto properties are turned into backing fields with getter/setter by the compiler. – Greg Bair Sep 4 '18 at 10:28
9

YAGNI isnt a principle, its an excuse for not doing stuff.

Here we can use the syntactic sugar

public DataAccess Service { get; set; }

and its cost us 10 characters worth of typing to get the benefits of a property over a field.

Maybe in the old days you could get away with fields, but so much stuff expects properties now, You are going to need it.

Get into the habit of doing stuff in full and not taking shortcuts and you just won't notice the extra effort.

  • 2
    "YAGNI isnt a principle, its an excuse for not doing stuff." - Ha, ha, ha! +1 for the first line alone! YAGNI. The cry of a truly lazy engineer (and not the good kind of lazy, either). – Greg Burghardt Aug 24 '18 at 16:51
  • 9
    To be fair, YAGNI is for architecture astronauts who build empty rooms in buildings that will never get used. – Robert Harvey Aug 24 '18 at 17:00
  • 3
    I disagree - I think YAGNI is aimed at less experienced people who will force all kinds of patterns and meaningless interfaces and whatnot in their code, creating a complicated mess in the process, because they don't understand the reasoning behind the things they read or heard regarding best practices, design and architecture. – Filip Milovanović Aug 24 '18 at 17:07
  • 2
    @GregBurghardt: actually, I would give -1 for the first line alone (but +1 for the third, which makes 0). People who think that way about YAGNI tend to overdesign things on a daily basis. – Doc Brown Aug 24 '18 at 18:18
  • 1
    @Ewan: then IMHO you should not write such rants against the YAGNI principle. Others could easily cite you as an excuse for implementing tons of stuff "just in case". – Doc Brown Aug 24 '18 at 19:54
5

Validation and logging are not really the main reason for using properties. How often have you actually used them for this purpose? I have very rarely. There are, however, several much more important reasons to use them:

  • encapsulation: by having a getter/setter, you have just a single "entry point" for accessing your field. You know that some other piece of code can only access the field through them. You can later change the implementation and users of the class won't even notice. For example, your getter might change between returning the value of field, calculating the value on the fly from other fields, or calculating it but also caching the value for later use. Or you have your data in a list, but later decide that an array would be better for some reason. If you start off with pure field access, you don't control how other code uses that field and you can never change the implementation.
  • mocking/stubbing: accessors are methods, so they can be mocked by a testing framework, which fields can not
  • allowing behavior to be changed in subclasses
  • since they are methods, they can be modified by code generation libraries or aspect-oriented programming; You could, for example, add performance metrics or access control to all getters which match certain criteria using a single aspect in AOP; your web framework or commons library might even be doing this for you
  • it is a common convention - other programmers expect you to use this style which makes mutual understanding of code easier; sometimes consistency is more important than being optimal
  • again as a convention, many frameworks can automatically perform certain operations on classes which conform to the property model - for example such classes may automatically be mapped to DTOs for storing in a database, deserialized to and from JSON, etc. Some frameworks may work this way with fields as well but others may not, both for technical and conventional reasons.

So, actually, there is a penalty for waiting. By not using properties, you lose the benefits listed above. And since it is very hard to change once the version with raw field access is out in the wild, it's better to start out with properties. The cost is minimal but there is much to lose if you happen to need any of the above points later on.

3

I just discovered in C# you can get the best of both worlds with auto property. For example,

public class AuditController
{
    public DataAccess Service { get; set; }
}

It gives you the warm feeling of encapsulation, but without the redundancy of option B.

0

By themselves, the code examples you've provided don't let you decide on this, because without context (the rest of the system, the evolution of the design of the system), it's more or less meaningless to talk about the details of how to encapsulation, except vaguely, in terms of best practices.

A good OO class will often not have many properties (there will not be a 1-to-1 mapping between the underlying representation and the public interface); instead, it will have a number of methods that perform some operation (you can ask the object to "do something"), and these methods will not expose much of what's going on inside the object.

Then there are those "objects" that are just data bags, with little to no behavior. These are often found at the boundaries of different components/subsystems. In this case, you may decide that getters and setters are a waste of time, or you may decide to have them because of practical reasons, depending on the language and the tools you are using (e.g., it could make the debugging easier).

As for YAGNI - it's a general principle, not a law of the universe. You kind of have to figure out what's its domain of applicability, and there will be some edge cases the benefits of breaking it outweigh the cost.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.