C# => Ruby Culture Shock #1: ‘Private’ Methods Ain’t So Private

This is the first in what I hope to be a series of little posts about the little flashes of ‘culture shock’ that I experience as I start to move from C# to Ruby as my day-to-day programming language.

Here we’re going to look at the subtle yet significant difference in how method access modifiers work in Ruby and C#.

In C#, you have four levels of scope to define who can call a method in your class:

  • Public
  • Internal
  • Protected
  • Private

In Ruby, you have three scopes with the exact same names:

  • public (the default, if none is specified)
  • protected
  • private

Public methods are public methods whatever language you use. Internal is a .NET specific thing to do with assembly boundaries, has no Ruby equivalent, and plays no more part in this discussion. It’s the behaviour of Protected and Private that we want to dig into.

Let’s try an example.

Starting in C#, suppose you have a base class for Animals, which allows clients to pat it:

public class Animal { public void Pat() { MakeNoise(“mmmmm”) }

private void MakeNoise(string noise) { // … talk to voicebox interface… } }

When an animal is patted, it makes a happy sound. Aw.

Now the actual mechanics of making that sound, nobody needs to know about, so I make the MakeNoise method private.

If I subclass Animal to create a Chimp, and I want a chimp to be able to make more noises, I can’t. MakeNoise is private to the Animal class, and in C# that means it’s hidden from any caller other than the class it is defined in.

So this code won’t compile:

public class Chimp : Animal { public void Tickle() { // This line won’t compile, because MakeNoise is private to the Animal class MakeNoise(“heehee”); } }

Do the same thing in Ruby, though, and Chimp can call the so-called private method on its superclass. Let’s fire up an IRB session and do it in there:

> class Animal > private > def makenoise(noise) > puts noise > end > end => nil > class Chimp < Animal > def tickle > makenoise ‘heehee’ > end > end => nil > Chimp.new.tickle heehee => nil

So it seems that the private make_noise method in Animal isn’t hidden from its subclasses.

Just to prove we did actually make the method private, let’s try calling it from outside the class hierarchy:

> Animal.new.makenoise ‘boo’ NoMethodError: private method `makenoise’ called for # from (irb):13 from :0

Of course if I wanted the same behaviour in C#, I’d have to change the scope of MakeNoise() to protected, thereby sharing it with subclasses of Animal.

In contrast, the protected modifier in Ruby will allow different instances of Animal (or its descendants) to call one another’s make_noise method.

Going back to our previous IRB session, we can re-open the Animal class to add such behaviour:

> class Animal > def poke(otheranimal) > otheranimal.make_noise ‘ouch’ > end > end

Now this won’t work until we modify the access to make_noise, so again we re-open the class:

> class Animal > protected :make_noise > end

Note that what we’re doing here is calling a built-in method called ‘protected’ from within the class to change the scope of the Animal#make_noise method. But I digress…

Still with me? Right, now we can create a horse, then get another animal to poke it:

> horse = Animal.new => # > Animal.new.poke(horse) ouch => nil

But, if I try to hit that make_noise method myself, I still can’t get in:

> horse.makenoise ‘eek’ NoMethodError: protected method `makenoise’ called for # from (irb):25 from :0

The protected make_noise method is only available to instances of Animal.

Can we do this in C#? Yes we can. In fact, we can do this in C# if the method is private scoped – because in C# private means ‘only visible to this class’.

To understand this difference, I found it helpful to recognise a new concept about the way method calls in Ruby work. Rather than just talking about objects and methods, Ruby programmers tend to talk about objects, methods and messages. By calling horse.makenoise, I’m actually using the ‘dot operator’ to send the horse object a message that says ‘please execute the makenoise method’.

This subtle difference really opens things up.

Marking the method private basically means that the message is blocked if it comes from any sender outside the class which is receiving the message. Notice how when I called the private method from the Chimp class, there was no dot operator:

> class Chimp < Animal > def tickle > make_noise ‘heehee’ > end > end

The method is found within the ‘private’ Chimp < Animal class hierarchy and so can be called without having to specify a receiver.

And that’s what counts with method scope in Ruby – it’s all about who is allowed to send the object messages to execute the method. For public methods, messages from anyone will execute the method (assuming it exists). For protected methods, messages from instances of the class (or subclasses) will be accepted, but anyone else will be politely declined. For private methods, the message needs to be sent ‘internally’, from a scope where the method can be found without having to send a message across objects using the ‘dot operator’.

Phew. That worked out quite a bit longer than I’d expected! Does it make sense to you?