Reputation:
Consider this silly program that does nothing:
interface I<out T> { }
class A1 : I<A1> { }
class A2 : A1, I<A2> { }
class B1 { }
class B2 : B1, I<B2> { }
class C1 : I<A1> { }
class C2 : C1, I<A2> { }
static class Program
{
static void f<T>(I<T> obj)
{
}
static void Main()
{
f<A1>(new A2());
f<A2>(new A2());
f<B1>(new B2());
f<B2>(new B2());
f<A1>(new C2());
f<A2>(new C2());
}
}
This shows that A2
and C2
implement both I<A1>
and I<A2>
, and that B2
implements both I<B1>
and I<B2>
.
However, modifying this to
static void Main()
{
f(new A2());
f(new B2());
f(new C2());
}
shows that on the first and third lines, f
's generic type argument cannot be inferred from the passed argument, yet on the second line, it can be.
I understand what it is that the compiler is doing here, so that doesn't need explaining. But how can I work around this? Is there some way to modify this so that I can define the interface on both the base and the derived class, yet have type inference work when passing the derived class?
What I had in mind was to look for a way to "hide" a base class's implemented interfaces, so that the compiler doesn't see them and use them, even though they do exist. However, C# doesn't seem to provide an option to do so.
Clarification: in my silly example program, A1
implements I
with itself as the generic type argument. I do have that in my real code, but I also have classes that implement I
with a different generic type argument, and have added C1
and C2
to my example code for that reason.
Upvotes: 10
Views: 423
Reputation: 7342
Using two F variants (second one just for type inference that calls the other) and an "override" interface J inheriting from I which does nothing it could be done like so:
using System;
using System.Threading;
interface I<out T>
{
void Print();
}
interface J<out T> : I<T> { }
class A : I<C>
{
void I<C>.Print()
{
Console.WriteLine("A: I<C>");
}
}
class B {}
class C : B { }
class D1 : I<A>
{
void I<A>.Print()
{
Console.WriteLine("D1: I<A>");
}
}
class D2 : D1, J<B>
{
void I<B>.Print()
{
Console.WriteLine("D2: I<B>");
}
}
class D3 : D1, J<C>
{
void I<C>.Print()
{
Console.WriteLine("D3: I<C>");
}
}
class D4 : A, J<B>
{
void I<B>.Print()
{
Console.WriteLine("D4: I<B>");
}
}
static class Program
{
static void f<T>(J<T> obj)
{
f((I<T>)obj);
}
static void f<T>(I<T> obj)
{
obj.Print();
}
static void Main()
{
f<A>(new D2());
f(new D2());
f(new D3());
f(new D4());
f<C>(new D4());
Console.ReadKey();
}
}
Output:
D1: I<A>
D2: I<B>
D3: I<C>
D4: I<B>
A: I<C>
Upvotes: 3
Reputation:
Just for reference, here's the best I've come up with. I don't really like it that much, but I have kept it as a least-bad option, for when reworking the code to avoid a need for this doesn't work.
interface I<out T> { }
class A1 : I<A1> {
public I<A1> PreferredInterface
{ get { return this; } }
}
class A2 : A1, I<A2> {
public new I<A2> PreferredInterface
{ get { return this; } }
}
class B1 { }
class B2 : B1, I<B2> {
public I<B2> PreferredInterface
{ get { return this; } }
}
class C1 : I<A1> {
public I<A1> PreferredInterface
{ get { return this; } }
}
class C2 : C1, I<A2> {
public new I<A2> PreferredInterface
{ get { return this; } }
}
static class Program
{
static void f<T>(I<T> obj)
{
}
static void Main()
{
f<A1>(new A2().PreferredInterface);
f<A2>(new A2().PreferredInterface);
f<B1>(new B2().PreferredInterface);
f<B2>(new B2().PreferredInterface);
f<A1>(new C2().PreferredInterface);
f<A2>(new C2().PreferredInterface);
f(new A2().PreferredInterface);
f(new B2().PreferredInterface);
f(new C2().PreferredInterface);
}
}
Upvotes: 1
Reputation: 5908
Lambda expressions may sometimes be helpful in selecting Generic argument type. This is a little convoluted way, but it let your A1 and A2 classes to decide, which interface would be chosen in the f2 call:
interface IChooseGenericArg<T>
{
T Choose();
}
interface I<out T>
{
string M1();
}
class A1 : I<A1>, IChooseGenericArg<A1>
{
public string M1()
{
return "A1.M1";
}
public A1 Choose()
{
return this;
}
}
class A2 : A1, I<A2>, IChooseGenericArg<A2>
{
public string M1()
{
return "A2.M1";
}
public new A2 Choose()
{
return this;
}
}
static class Program
{
static void f<T>(I<T> obj)
{
Console.WriteLine(obj.M1());
}
static void f2<T>(Func<T> selector, I<T> obj)
{
Console.WriteLine(obj.M1());
}
static void Main()
{
f<A1>(new A1());
f<A2>(new A2());
var a2 = new A2();
f2(() => a2.Choose(), a2);
var a1 = new A1();
f2(() => a1.Choose(), a1);
Console.ReadLine();
}
}
Upvotes: 1
Reputation: 6684
You could try changing the signature of f
and using a generic constraint to effectively get similar behavior within f
:
static void f<T>( T obj ) where T : I<T>
{
}
It's difficult to tell if this will actually allow you to accomplish what you want, however. This also won't work for types like C2
below:
class C1 : I<C1> { }
class C2 : C1 { }
If you need to be able to handle these cases, I think I'd have to recommend just specifying the type arguments explicitly. I can't think of a way around this.
Upvotes: 0