Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to create a method that returns one of two possible types?

I have 2 data structures: Dictionary<string, string> and Multimap<string, string>. Multimap is really just a Dictionary under the hood. I took must of the code from this question. Here's the class definition:

public class Multimap<TKey, TValue> : Dictionary<TKey, HashSet<TValue>>
{ ... }

Both data structures have a .Add(TKey key, TValue value) method.

I have a class that is responsible for populating these maps from certain files. I currently have the following two methods:

    public Dictionary<string, string> PopulateDictionary(...)
    {
        Dictionary<string, string> returnDictionary = new Dictionary<string, string>();
        ...
        foreach (...)
        {
            ...
            returnDictionary.Add(key, value);
        }
        return returnDictionary;
    }

    public Multimap<string, string> PopulateMultimap(...)
    {
        Multimap<string, string> returnMultimap = new Multimap<string, string>();
        ...
        foreach (...)
        {
            ...
            returnMultimap.Add(key, value);
        }
        return returnMultimap;
    }

As you can see, they're exactly the same, both around 25 lines long, and the only difference is their return type. What I am looking to do is condense this into one method. My first attempt was to have the method

public Dictionary<string, object> PopulateGenericDictionary(...)
{ ... }

Where object was either string or HashSet<string>. But I didn't have much luck casting from Dictionary<string, object> to Multimap<string, string>.

Extracting the logic out of the methods is an option, but it's not great. Because of the foreach loops, there's always going to be some logic inside the two methods. You do end up with methods that are twice as small, but there's still two identical methods, which doesn't truly solve the problem.

This would be my ideal method structure:

public Dictionary<string, string> PopulateDictionary(...)
{
    return MethodThatDoesAllTheLogic(...);
}
public Multimap<string, string> PopulateMultimap(...)
{
    return MethodThatDoesAllTheLogic(...);
}
public ??? MethodThatDoesAllTheLogic(...)
{ ... }

I've been fiddling around with casting and generics, but I just can't get it to work. Any ideas?

Edit

I have used millimoose's solution. Here's my code now:

    public Dictionary<string, string> GenerateDictionary(...)
    {
        Dictionary<string, string> returnMap = new Dictionary<string, string>();
        PopulateDictionary(returnMap.Add, ...);
        return returnMap;
    }

    public Multimap<string, string> GenerateMultimap(...)
    {
        Multimap<string, string> returnMap = new Multimap<string, string>();
        PopulateDictionary(returnMap.Add, ...);
        return returnMap;
    }

    private static void PopulateGenericDictionary(Action<string, string> addFunc, ...)
    {
        ...
        foreach (...)
        {
            addFunc(key, value);
        }
    }

Much cleaner!

like image 656
Tom Avatar asked Jan 15 '23 04:01

Tom


1 Answers

To work around the lack of a common interface, you can invent one ad-hoc using a bunch of delegate type parameters:

void MethodThatDoesAllTheLogic(Action<string, string> addFunc)
{
    // ...
    addFunc(key, value);
    // ...
}

public Dictionary<...> PopulateDictionary()
{
    // ...
    MethodThatDoesAllTheLogic(result.Add);
}

(Adding more parameters as necessary.)

like image 144
millimoose Avatar answered Jan 22 '23 10:01

millimoose