Using C# Delegates to Translate Text Into Geek Speak!

Much like my article on using extension methods, I want to talk about another cool feature of C# known as delegates, but to make it more useful, I want there to be a purpose to it. Let’s get ready, let’s get ready, let’s get ready to Geek Speak!

Years ago I remember we used to play around with these UNIX utilities that would take as input a text stream and then translate it into other languages. I don’t mean anything serious like French or German but instead into Shakespearean or “Redneck“.

For example, running this:

might produce something like:

in a cockney translator. All very useful!

Let’s Talk Geek

Let’s think about how we might achieve something like that, but for Geek Speak. If you don’t already know, Geek Speak is (partly) where some letters are replaced with numbers. In my version, I also want it to be in lower case, based on this mapping:

Here’s an example:

There are lots of other translations of characters that could be made, but that’s enough Geek for now!

Doing It The Inflexible Way

If we wanted to write a program that did this, we might come up with something like this:

We simply create an object of type err…SimpleTranslator and then invoke the DoTranslation method to return the newly created string. In fact, given that the function requires no state, a static function would be even better.

However, what if we wanted to translate the text into another language such as Uppity (a little known language were all the characters are in uppercase)? We could add a new method like this:

Or what about keeping the same method, but adding a selection statement?

Erm…let’s just ignore the method we use to choose which language here – using a string is just for illustrative purposes and fairly shocking! Please?! The point is, it doesn’t look too good.

Also, it isn’t all that flexible. What if we wanted to use a different class or other methods to translate? Right now, we can’t because we’re tied to SimpleTranslator.

Another point worth mentioning is that we are baking all this logic inside the translator. It would be so much better if we could extract that and put the onus on the user of the class to provide their own translation function. In a sense, this is inversion of control.

All in all, not particularly desirable.

Starting To Delegate

Of course, there is a better way and that is by using delegates.

So what are they?

A delegate is a type that represents references to methods with a particular parameter list and return type. When you instantiate a delegate, you can associate its instance with any method with a compatible signature and return type. You can invoke (or call) the method through the delegate instance.

 

— https://msdn.microsoft.com/en-us/library/ms173171.aspx

Or, put another way, they are a bit like C’s pointers to functions. In the language C, it is possible to create a variable which can point to memory or, as in this case, a block of code which you can then execute. C is good like that, but very dangerous, naturally!

C# however is much more careful thanks to type safety and checking, which will ensure that you can only point to valid (read: safe) things.

In our case, we’re going to create a delegate and assign a function to it which will do all the hard work of translating our text. As far as the translating class is concerned, it doesn’t care, so long as it points to a valid method. This is best shown with an example:

Let’s break this down a little.

On line 14 we are declaring the delegate and basically saying that our new type is to be called TranslatorFunctionType and that it is allowed to point to functions that take one parameter (a string) and will return a string. What can make this seem difficult to read is that it also looks just like a method signature. Tricky!

Line 18 is just an auto-property that allows us to assign the method to the delegate variable. It didn’t need to be done this way, but I think it’s nicer. Another option would be something like this:

We would need to expose the field publicly and assign the function with something like:

Line 22 is where the magic happens. Once the delegate field has been assigned, we can use it just like a method and call it. Super-cool!

Lastly, line 9 is where we actually assign the method to the delegate. In this case, we use a method inside the class GeekTranslator. It didn’t need to be in a class – I just chose that for this example.

Before we continue, we can take a quick look at what the output looks like. Pictures do make the world a better place!

Image showing output from running the program

Output

Another nice feature of delegates is that we don’t actually have to use the new keyword when assigning to the delegate. C# is clever enough to understand this:

Adding Another Translator

Now, this is so much easier. Let’s add our Uppity translator.

And we can call it using this easy change:

Note the differences on the highlighted lines.

Chaining Delegates With += and -=

You might also create a chain of delegates that can all be called with one method invocation. Here’s how it would look to make this easier to visualise:

In our case, this won’t work and only the last translator will function (try it and see), but in other situations, you can imagine methods with a void signature doing all kinds of interesting things.

Using Anonymous Delegates

You don’t have to use fully labelled methods though. A handy thing is that we are able to use anonymous methods when assigning to the delegate which is a nice alternative if you don’t want the overhead. Here’s one way you could achieve that for the Geek translator:

Summary

Delegates are pretty amazing, really! I have only scratched the surface of what you can do with them, but hopefully it gives you a taste and encourages you to learn a bit more and use them in your projects.

H0p3 y0u 3nj0y3d it!

twittergoogle_plusredditpinteresttumblrmail

Written by Stephen Moon
email: stephen at logicalmoon.com
www: https://www.logicalmoon.com


This entry was posted in: c#. Bookmark the ➜ permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *