public interface IFoo { }
public abstract class IFooDerived : IFoo { }
public class Foo : IFooDerived { }
public interface IBar { }
public abstract class IBarDerived : IBar { }
public class Bar : IBarDerived { }
public interface IExample<out A, B> where A : IFoo where B : IBar
{
A Foo(B b);
}
public class Example : IExample<Foo, Bar>
{
static Example example = new Example();
static Example()
{
Example ex = example;
IExample<IFoo, IBar> ex_huh = ex; //How can I do this?
//Or is there a way where I can say I don't care, but I still want to reference ex, like this:
IExample<object,object> ex_huh2 = ex;
//Or:
IExample<, > ex_huh2 = ex;
ex_huh2.Foo(new Bar()); //And still call Foo?
}
public Foo Foo(Bar b)
{
throw new NotImplementedException();
}
}
In the above example, how do I downcast and reference the static example variable without knowing its generic types but still be able to call "A Foo(B b)"?
Is there a strongly typed way of doing this?
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.