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 just a dictionary under the hood. I took 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 cards from specific 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 are exactly the same as about 25 lines, and the only difference is their return type. What I'm going to do is condense this into one method. My first attempt was to have a method

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

Where object was either string or HashSet<string> . But I was not lucky with Dictionary<string, object> to Multimap<string, string> .

Extracting logic from methods is an option, but it is not very convenient. Due to foreach loops, there is always some kind of logic inside two methods. As a result, you get methods that are half as much, but there are two more identical methods that really do not 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 do casting and generics, but I just can't get it to work. Any ideas?

Edit

I used a millimose solution. Here is 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); } } 

Significantly cleaner!

+4
source share
6 answers

To get around the lack of a common interface, you can come up with one ad-hoc using a bunch of parameters like delegate:

 void MethodThatDoesAllTheLogic(Action<string, string> addFunc) { // ... addFunc(key, value); // ... } public Dictionary<...> PopulateDictionary() { // ... MethodThatDoesAllTheLogic(result.Add); } 

(Add more options if necessary.)

+8
source

I would avoid having the helper method create an actual collection at all; just fill out the existing collection. This can be done much more efficiently, since the Add method has the same signature in both cases. We can simply use the delegate to accept the Add method:

 public static void PopulateMapping<TKey, TValue>(Action<TKey, TValue> addMethod, IEnumerable<TKey> data) //include other parameters needed to populate the data { foreach (var key in data) { addMethod(key, default(TValue)); } } 

Then it will be used as follows:

 public static Dictionary<string, string> PopulateDictionary() { Dictionary<string, string> output = new Dictionary<string, string>(); PopulateMapping<string, string>(output.Add, new string[] { "a" }); return output; } 
+3
source

If you are looking only for the Add method, then both objects should share an IDictionary . However, the Add method uses only objects. Most likely, this is the closest thing that you can get without using generics in the method ... but again you are losing the benefits of generics at this point.

0
source

See if this approach is useful: The key is to abstract when creating the object (Dictionary or Multimap) and get the values ​​- two differences in the filling method.

 public Dictionary<string, TValue> Populate<TValue>( Dictionary<string, TValue> returnDict, Func<SomeType, TValue> valueProvider) { string key = null; ... foreach (...) { ... returnDict.Add(key, valueProvider(value)); } return returnDict; } 

An example call might be:

 public void Test() { Populate(new Multimap<string, HashSet<string>>(), (t) => new HashSet<HashSet<string>>()); } 

I'm not sure if the valueProvider delegate will match your issue. Try to give more information about this.

0
source

If your internal logic is really identical, except that the type is TValue - and I mean word for word, then you can do something like:

 IDictionary<string, TValue> MethodThatDoesAllTheLogic<TValue>(whatever) { // word for word-identical logic } 

I used the TValue method as the only type parameter, because this is the only difference (in the example that you showed): both methods have a string as a parameter of the first type.

ETA: It is assumed that MultiMap implements IDictionary<K,V> . Since you said it was inherited from Dictionary<K,V> , I assumed that it did.

-1
source

in C # with the help of generics, you can require them to extend or implement a certain class in our case Dictionary, as follows, how can you achieve this.

 public T Populate<T>(string val) where T : Dictionary<string, string>, new() { T returnDict = new T(); returnDict.Add("key", "val"); return returnDict; } 
-1
source

All Articles