Declaration

I am going to create a store variable of type Dictionary which holds the kilograms of fruit that we have in store for each fruit type.

We can create a Dictionary like so:

var store = new Dictionary<string, double>();

Adding items

Then, we can start adding new items to the Dictionary (remember, you need to provide first the key, and then the value):

store.Add("peach", 4.5);
store.Add("lemon", 10);
store.Add("grape", 14.8);

TIP

The Key is first, the Value is second.

This is the power of dictionaries: they allow you to associate unique keys with values.

Object initializers

But we can do these steps at once with object initializers:

var store = new Dictionary<string, double>
{
    { "peach", 4.5 },
    { "lemon", 10 },
    { "grape", 14.8 }
};

Index initializers

Also, there is another approach of initializing a Dictionary in C#, and that is with the index initializers:

var store = new Dictionary<string, double>
{
    ["peach"] = 4.5 ,
    ["lemon"] = 10,
    ["grape"] = 14.8
};

Looping

Let's try to print out the kilograms for each fruit we have to the console by using the foreach loop:

foreach (var item in dictionary)
{
    Console.WriteLine($"{item.Value} kilograms of {item.Key}s");
}

And we get this result:

4.5 kilograms of peachs
10 kilograms of lemons
14.8 kilograms of grapes

TIP

The actual type of the item variable from the previous foreach loop is: KeyValuePair – which represents exactly that it says: a pair of a Key and a Value. So, for us the Key is the fruit name (lemon 🍋 for example), and the value is the quantity (4.5 kilograms of lemons 🍋).

Unic keys

Now, let's try adding more grapes 🍇 to our store. Maybe we might have forgotten that we just initialized our Dictionary with grapes already and we could do something like this:

store.Add("grape", 7);

ArgumentException

And we get an exception: System.ArgumentException: 'An item with the same key has already been added.'

So, the keys must be unic (if you thing about it, this is why I didn't choose to use quantity as my key because we might have similar quantities for same fruit types).

The best practice is to check whether or not the Key exists in the Dictionary before trying to add it. Let's see how that is done.

First, we an keep the line from before, but we need to check if the key is not present in hte Dictionary with the ContainsKey method:

if (!store.ContainsKey("grape"))
{
    store["grape"] = 7;
}

Then, on the else branch we can increase the quantity of grapes 🍇 because we know that we have them in the store:

else
{
    var value = store["grape"];
    value += 7;
    store["grape"] = value;
}

And this works 😄 Now, we have 21.8 kg of grapes.

Refactoring

Let's refactor because we have use the string grape way too many times and 7 for quantity as well:

var grapeType = "grape";
var grapeQuantity = 7;

if (!store.ContainsKey(grapeType))
{
    store[grapeType] = grapeQuantity;
}
else
{
    var value = store[grapeType];
    value += grapeQuantity;
    store[grapeType] = value;
}

Now, let's add some more lemons 🍋. And we have to do the same thing 😱. Well, not really. This is where we can extract a function which does this: it adds quantity of fruits.

  1. Add a new line to add fruit quantity and name it just like that: AddFruitQuantity:
AddFruitQuantity(store, grapeType, grapeQuantity);
  1. Click on the name, press Ctrl and . and press Enter to generate the method.
private static void AddFruitQuantity(Dictionary<string, double> store, string grapeType, int grapeQuantity)
{
    throw new NotImplementedException();
}
  1. We can extract the checking functionality and moving it into our new function:
private static void AddFruitQuantity(Dictionary<string, double> store, string grapeType, int grapeQuantity)
{
    if (!store.ContainsKey(grapeType))
    {
        store[grapeType] = grapeQuantity;
    }
    else
    {
        var value = store[grapeType];
        value += grapeQuantity;
        store[grapeType] = value;
    }
}

Now, let's add lemons as well:

AddFruitQuantity(store, "lemon", 3);

TIP

Notice that we didn't have to create separate variables again (we could though), but now we pass them as arguments to the function and we don't need to repeat ourselves.

Functions

Because we might use certain functionality in the future, we have functions which helps us reuse our code multiple times.

Extension methods

You know me that I want to push you guys a little bit. What about being able to call this method AddFruitQuantity like this:

store.AddFruitQuantity(grapeType, grapeQuantity);

instead of this:

AddFruitQuantity(store, grapeType, grapeQuantity);

Isn't it more expressive, more declarative?

Well, we can.

  1. mark the store parameter with the this keyword:
private static void AddFruitQuantity(this Dictionary<string, double> dictionary, string key, int value)
  1. make a separate static class called: DictionaryExtension (this is what we do: we extend the dictionary class) and move the function there:
static class DictionaryExtension
{
    public static void AddFruitQuantity(this Dictionary<string, double> dictionary, string key, int value)
    {
        if (!dictionary.ContainsKey(key))
        {
            dictionary[key] = value;
        }
        else
        {
            var val = dictionary[key];
            val += value;
            dictionary[key] = val;
        }
    }
}

Now, we can call these as we wanted:

store.AddFruitQuantity(grapeType, grapeQuantity);
store.AddFruitQuantity("lemon", 3);

Notice, that we do not need to pass the store as the first parameter.

Extension methods

This is called an extension method and it is a very popular way of extending the functionality of a class.

Refactoring

Because this function turned to be generic, we can rename it to IncreaseValue. In this way, we can use this not only for fruits, but for cars, books, anything we might create in the future.