考虑一下这个没有任何作用的愚蠢程序:
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());
}
}
这表明A2
和C2
同时实现了I<A1>
和I<A2>
,B2
同时实现了I<B1>
和I<B2>
}。
但是,将其修改为
static void Main()
{
f(new A2());
f(new B2());
f(new C2());
}
表明在第一行和第三行,f
的泛型类型参数不能从传递的参数推断出来,但在第二行,它可以是。
我理解编译器在这里做的是什么,因此不需要解释。但是我该如何解决这个问题呢?有没有办法修改它,以便我可以在基类和派生类上定义接口,但在传递派生类时有类型推断吗?
我想到的是寻找一种方法来“隐藏”基类的实现接口,这样编译器就不会看到它们并使用它们,即使它们确实存在。但是,C#似乎没有提供这样做的选项。
澄清:在我的愚蠢示例程序中,A1
将自身作为泛型类型参数实现I
。我确实在我的真实代码中有这个,但是我也有类使用不同的泛型类型参数实现I
,并且因为这个原因将C1
和C2
添加到我的示例代码中。
答案 0 :(得分:3)
使用两个F变体(第二个仅用于调用另一个的类型推断)和一个&#34;覆盖&#34;继承自I的接口J没有做任何事情,它可以这样做:
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();
}
}
输出:
D1: I<A>
D2: I<B>
D3: I<C>
D4: I<B>
A: I<C>
答案 1 :(得分:1)
Lambda表达式有时可能有助于选择Generic参数类型。这是一个有点复杂的方式,但它让你的A1和A2类决定,在f2调用中选择哪个接口:
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();
}
}
答案 2 :(得分:1)
仅供参考,这是我提出的最好的。我并不是那么喜欢它,但是我把它作为一个最不好的选择,因为当重新编写代码以避免需要时它不起作用。
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);
}
}
答案 3 :(得分:0)
您可以尝试更改f
的签名并使用通用约束在f
内有效地获得类似行为:
static void f<T>( T obj ) where T : I<T>
{
}
很难说这是否真的能让你完成你想要的任务。这也不适用于下面的C2
类型:
class C1 : I<C1> { }
class C2 : C1 { }
如果您需要能够处理这些情况,我想我必须建议明确指定类型参数。我无法想办法解决这个问题。