IExample<IFoo, IBar> ex_huh = ex; //How can I do this?
You can't, because it would break type safety. And that's because you're trying to make your method's parameter type (that parameter to Foo(B b)
) more general, and that's not safe.
To make it easier to see why that's a problem, let's boil your example down to something equivalent (as far as that method parameter goes):
function DoSomethingWithFoo(Foo foo) { ... }
// The following line won't compile, but let's pretend it does and see what happens
Action<IFoo> doSomethingWithIFoo = DoSomethingWithFoo;
doSomethingWithIFoo(new OtherFoo());
where OtherFoo
is some other class that implements IFoo
, but does not descend from the Foo
class.
Whoops! You're calling DoSomethingWithFoo
, which expects an instance of Foo
, but passing an OtherFoo
instead! Wacky hijinks ensue.
That's essentially what you're trying to do in your example. You're trying to take a class with a method that expects a parameter of the Foo
type, and cast that to something that lets you pass any IFoo
instead.
(And that's why the compiler won't let you declare IExample
's B
type parameter as out B
, which is what it would have to be to let you cast an IExample<..., Foo>
to an IExample<..., IFoo>
. The compiler sees that the B
type parameter is used as the type of a method parameter, and that therefore, making it covariant would break type safety.)
As for how to accomplish what you're looking for: that's going to depend. Your specific example of wanting to do
IExample<...figure out how to declare this...> = ...any IExample<A, B>...;
ex_huh2.Foo(new Bar());
isn't going to work in general, because that "...any IExample<A, B>
..." might well be an IExample<..., OtherBar>
, and then you can't pass it a new Bar()
. You'll have to figure out how you want to resolve that conflict.
Maybe you really do want to pass in a new Bar()
, in which case maybe you want to do that inside a method that's constrained to only taking IExample
s with Bar
as their second type parameter:
public void AddABar<TFoo>(IExample<TFoo, Bar> example)
{
example.Foo(new Bar());
}
Or maybe you want to create a new instance of whatever the second type parameter is:
public void AddAThing<TFoo, TBar>(IExample<TFoo, TBar> example)
where TBar : new()
{
example.Foo(new TBar());
}
Or maybe you want to make IExample<A, B>
descend from a non-generic IExample
that declares a non-generic Foo(IBar b)
, implement that non-generic method on your Example
class, and do a typecast to B
inside that method -- taking the risk that if you pass the wrong type into that method at runtime, you'll get an InvalidCastException
.
It really all comes down to how you want to resolve that conflict of "I want to cast this to something I can pass a new Bar()
to", when in point of fact any given implementation of IExample<A, B>
won't necessarily be able to accept a new Bar()
as a method parameter.