29

The Law of Demeter states the following:

  • Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
  • Each unit should only talk to its friends; don't talk to strangers.
  • Only talk to your immediate friends.

C# 6.0 introduced a new operator called the null-conditional operator. IMHO, it makes coding easier and improves readability. But it also makes it easier to write more coupled code, as it is easier to navigate through class fields, already checking for nullity (something like var x = A?.B?.C?.D?.E?.F?).

Is it correct to state that this new operator goes against the Law of Demeter?

  • 2
    Why do you believe that A?.B?.C?.D?.E?.F? would violate it - LoD is not about how many dots and if the calling method has such information about the structure that isn't in violation with its points, such a call would be perfectly acceptable. That such code could violate LoD isn't enough to say that all uses of it do violate LoD. – user40980 Oct 7 '15 at 18:25
  • 14
    Reading "The Law of Demeter Is Not A Dot Counting Exercise"? It discusses this exact example. – outis Oct 7 '15 at 20:09
  • @outis: excelent read. I am not saying that every code in the form of X.Y.Z.W.U is a violation to the "law". But, in my experience dealing with code, 90% of the time it is just plain ugly coupled code. – Arthur Rizzo Oct 7 '15 at 20:28
  • 2
    @ArthurRizzo but that isn't a problem with the null conditional operator going against LoD. That is the code that is at fault. The operator is just a tool to simplify the human reading it. The .? no more violates LoD than + or - does. – user40980 Oct 7 '15 at 20:59
  • 1
    RC Martin distinguishes between pure data classes and behavioral classes. If the accessed Properties expose internal data of a behavioral class the snippet certainly violates the LoD, but this has nothing to do with the null-conditional operator. Anyway, the properties are not bound to expose internal data, which might be a smell, but does not violate the LoD. According to RC Martin the schema may be absolutely valid with pure data classes. – Paul Kertscher Oct 8 '15 at 6:58
44

Is it correct to state that this new operator goes against the Law of Demeter?

No*


* The null conditional operator is a tool within the language and the .NET framework. Any tool has the ability to be abused and used in ways that could harm the maintainability of a given application.

But the fact that a tool can be abused doesn't necessarily mean that it has to be abused, nor that the tool violates any particular principle(s) that may be held.

The Law of Demeter and others are guidelines about how you should write your code. It's targeted to humans, not the tools. So the fact that the C# 6.0 language has a new tool within it doesn't necessarily affect how you should be writing and structuring your code.

With any new tool, you need to evaluate it as ... if the guy who ends up maintaining your code will be a violent psychopath .... Note again, that this is guidance to the person writing the code and not about the tools being used.

  • foo = new FiveDMatrix(); foo.get(0).get(0).get(0).get(0).set(0,1); would be fine (and no worse than foo[0][0][0][0][0] = 1)... and lots of other situations where such doesn't violate LoD. – user40980 Oct 7 '15 at 18:28
  • @MichaelT When you start getting into matrices of that dimensionality, it seems like it would become easier to treat the indices as a vector/tuple/array itself and let the internals of the matrix class worry about how the data is actually stored. (Which, now that I think about it, is an application of the Law of Demeter, at least as it relates to encapsulation.) – JAB Oct 7 '15 at 20:49
  • (And, of course, that sort of practice makes it easier to implement multidimensional slicing and have some really powerful matrix tools.) – JAB Oct 7 '15 at 21:03
  • 1
    @JAB I was just trying to come up with an example. A better one would likely be Dom file = prase("some.xml"); file.get(tag1).getChild().get(tag2).getChild() ... - its an issue of processing the structure of some dumb code. Its not a stranger... its just dumb. The .? becomes very useful in such structures. – user40980 Oct 7 '15 at 21:12
10

Sort of.

If you're only doing one access (a?.Foo) then it is equivalent to:

a == null ? null : a.Foo

which most people would agree is not a violation of the Law of Demeter. At that point, it is just syntactic sugar to improve readability.

Anything more than that, and it probably would violate the Law of Demeter, and this feature does tend to promote that sort of usage. I would even say that the above "good" usage alone isn't enough to warrant this sort of change to the language, so I expect that it was made to support the less clearly good usage.

That said, it is worth remembering that the Law of Demeter isn't a law per se, but more of a guideline. Lots of code violates it and works well. Sometimes the simplicity of design or of code is worth more than the risk posed by violating the Law of Demeter.

  • anything more than that does not necessarily break LoD e.g. builder pattern – jk. Oct 8 '15 at 8:08
  • @Telastyn: The new language syntax we are talking about does support method calls: a?.Func1(x)?.Func2(y) The null coalescing operator is something else. – Ben Voigt Oct 8 '15 at 14:19
  • @BenVoigt - ah, I was going off of the article, which indicated it only worked with fields, properties and indexers. I didn't have MSVS2015 handy to test with. You are right. – Telastyn Oct 8 '15 at 21:39
  • 1
    a?.Foo is not quite equivalent to a == null ? null : a.Foo. The former evaluates a only once, the latter evaluates it twice. That could matter if a were an iterator. – Loren Pechtel Oct 10 '15 at 2:48
9

No. Let's consider both the operator on its own, and the heavily chained use you have for it.

On its own .?A depends upon the same amount of knowledge of the class the left-value is and of the type returned by the method as .A != null does, viz. It needs to know about that the A property exists and returns a value that can be compared with null.

We can only argue that this violates the law of Demeter if typed properties do. We aren't even forced to have A as a concrete type (its value could be of a derived type). The coupling here is minimal.

Now lets consider var x = A?.B?.C?.D?.E?.F.

Which means that A must be of a type that could be null, or could have a B property, which must be of a type that could be null or have a C property, and so on until the type of the E property being something that could be null or could have an F property.

In other words, we need to be doing this with either a statically-typed language or have applied a constraint on the types that can be returned if the typing is loose. C# in most cases uses static typing, so we've changed nothing.

If we had then the following code would also violate the law:

ExplicitType x;
var b = A.B;
if (b == null)
  x = null;
else
{
  var c = b.C;
  if (c == null)
    x = null;
  else
  {
    var d = c.D;
    if (d == null)
      x = null;
    else
    {
      var e = d.E;
      if (e == null)
        x = null;
      else
        x = e.F;
    }
  }
}

Which is exactly the same. This code that is using the coupling of different elements needs to "know" about the full chain of coupling, but it is using code that doesn't violate the Law of Demeter to do so, with each unit having a well-defined coupling with the next.

  • 3
    +1 the new operator is merely syntactic sugar for the bitter recipe you've described. – Ross Patterson Oct 8 '15 at 11:37
  • 1
    Well, if a dev writes a code that looks like that I think it's easier to notice that something may not be right. I know that the operator is 100% syntatic sugar, but still, I think people tend to get more confortable writing something like var x = A?.B?.C?.D?.E?.F than all those if/elses even if they are the same in the end. – Arthur Rizzo Oct 8 '15 at 13:23
  • 2
    It's easier to notice something isn't right in A?.B?.C?.D?.E?.F because there's less that can be wrong; either we should be trying to get F via that path, or we shouldn't, while the longer form could have errors within it as well as the error of it not being the correct thing to do. – Jon Hanna Oct 8 '15 at 13:51
  • @ArthurRizzo But if you associate the above kind of code with LoD violations, then it's easy to miss them in the case where there's no null checking needed and you can just do A.B.C.D. It's much simpler to have a single thing to look out for (chained property access) rather than two different things which depend on a pretty irrelevant detail (null-checking) – Ben Aaronson Oct 9 '15 at 11:24
5

Object may be created for the purpose of encapsulating behaviors or holding data, and objects may be created for the purpose of being shared with outside code or held privately by their creator.

Objects which are created for the purpose of encapsulating behavior (whether shared or not), or for the being shared with outside code (whether they encapsulate behavior or data) should generally be accessed through their surface interface. When data-holding objects are created for use exclusively by their creator, however, the normal Law-of-Demeter reasons for avoiding "deep" access don't apply. If part of a class which stores or manipulate data in the object is changed in a way that would require adjusting other code, it will be possible to guarantee that all such code gets updated because--as noted above--the object was created for the exclusive use of one class.

While I think the ?. operator could perhaps have been better designed, there are enough situations where objects make use of nested data structures that the operator has many use cases which would not violate the principles expressed by the Law of Demeter. The fact that it could be used to violate the LoD should not be taken as an argument against the operator, since it is no worse than the "." operator in that regard.

  • Why does the Law of Demeter not apply to data holding objects? – Telastyn Oct 7 '15 at 21:10
  • 2
    @Telastyn: The purpose of the LoD is to avoid problems that may arise if a piece of code accesses inner objects that something else might manipulate or care about. If nothing else in the universe could possibly manipulate or care about the state of the inner objects, then there's no need to guard against such problems. – supercat Oct 7 '15 at 21:22
  • I'm not sure I agree. It's not that other things might care of modify the data, it's that you're coupling to the contained object via a path (essentially three points of coupling - the two objects and their relation). Sometimes that coupling is not going to be a big deal, but still seems smelly to me. – Telastyn Oct 7 '15 at 21:32
  • @Telastyn: Your point about paths is a good one, but I think my point holds just fine. Accessing an object via multiple paths creates coupling among those paths. If some access is via shallow path, then access via deep path as well may cause unwanted coupling. If all access is via one particular deep path, however, there won't be anything for that deep path to couple to. – supercat Oct 7 '15 at 23:20
  • @Telastyn It's perfectly fine to traverse a datastructure to get to data deep down. It is not the same as nested method calls. You are sometimes required to know a datastructure and how it is nested, the same does not go for objects like a service and its own nested services/repositories, etc. – Per Hornshøj-Schierbeck Oct 8 '15 at 9:23

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.