1

I have a method like this:

public void SomeMethod(Dictionary<IFoo, IBar> myDict) {}

and I have two classes that inherit from IFoo and IBar:

public class FooClass : IFoo {}
public class BarClass : IBar {}

I'm trying to use my method with a dictionary like below:

Dictionary<FooClass, BarClass> myDict = DeserializeFromJson();
SomeMethod(myDict);

But I'm getting the compile error

argument cannot convert from ... to ... ".

Can someone explain why such conversion is invalid?

Cleptus
  • 3,446
  • 4
  • 28
  • 34
adam.k
  • 324
  • 3
  • 14
  • 2
    Suppose `SomeMethod` started with `myDict[new OtherFoo()] = new OtherBar();` where `OtherFoo : IFoo` and `OtherBar : IBar`. How would you expect that to work when your `Dictionary` expects all keys to be `FooClass` and all values to be `BarClass`? – Jon Skeet Apr 29 '21 at 11:16
  • 1
    You mention inheritance, but looks like inheritance plays no role here, if you are using standard .net naming conventions you are working with interfaces – Cleptus Apr 29 '21 at 11:16
  • https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/ – Caius Jard Apr 29 '21 at 11:20

1 Answers1

5

See this question about converting from List<Dog> to List<Animal> for why you can't do this directly. Here's the dictionary-specific explanation, imagine that you can do:

// here, myDict is of type Dictionary<Foo, Bar>, as we are outside SomeMethod
// suppose myDict is empty initially
SomeMethod(myDict);

And then SomeMethod could do:

// here, myDict is of type Dictionary<IFoo, IBar>, as we are inside SomeMethod
myDict.Add[new AnotherClass1(), new AnotherClass2());

where

class AnotherClass1: IFoo {}
class AnotherClass2: IBar {}

After SomeMethod returns, you do:

// here, myDict is Dictionary<Foo, Bar> again, we are outside SomeMethod
Bar firstValue = myDict.Values.First();

Wait... firstValue should be of type AnotherClass2!

See the problem?

Rather than creating a new Dictionary<IFoo, IBar> with the same values,

Dictionary<IFoo, IBar> newDict = myDict.ToDictionary(x => x.Key, y => y.Value);

which could take some time (and the modifications to the dictionary that SomeMethod does won't reflect not the original dictionary), you can also make SomeMethod generic:

public void SomeMethod<TFoo, TBar>(Dictionary< TFoo, TBar> myDict) 
    where TFoo: IFoo
    where TBar: IBar 
{
    // the method can be implemented the same way as before
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313