使用 C# 理解嵌套泛型类,附测验

60

我和同事谈论 C# 时,他向我展示了一些 C# 代码,让我预测其输出。起初看起来很简单,但实际上却不是这样的。我真的不太明白为什么 C# 会表现出这种行为。

代码:

public class A<T1>
{
    public T1 a;

    public class B<T2> : A<T2>
    {
        public T1 b;

        public class C<T3> : B<T3>
        {
            public T1 c;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        A<int>.B<char>.C<bool> o = new A<int>.B<char>.C<bool>();

        Console.WriteLine(o.a.GetType());
        Console.WriteLine(o.b.GetType());
        Console.WriteLine(o.c.GetType());

        Console.ReadKey();
    }
}

输出结果为:

System.Boolean
System.Char
System.Int32

如果我错了,请纠正我,但是我理解o.a的类型是bool,因为C<T3>继承自B<T3>,而B<T2>则继承自A<T2>。我也稍微能够理解o.c的类型是int,因为c的类型是T1,它是从外部类中获取的(我想)。

当我尝试弄清楚为什么o.b的类型是char时,我的头差点炸了。有人能解释一下吗?


40
我很高兴我不需要在贵公司维护代码。 - default
4
为什么?因为他们喜欢互相考察奇怪的 C# 代码? - Erix
2
@default 这不是测验的本意吗? - dmaij
当执行:public class B<T2> : A<int> 时,得到了预期的结果,很有趣。 - MBen
4
添加第四个类清楚地表明它作为堆栈的功能。 - Nahum
显示剩余2条评论
2个回答

40

这是一道古老的谜题,非常难。当我把它交给Anders本人时,他第一次没能正确回答!

我想你的同事给你的版本来自Cyrus的博客:

http://blogs.msdn.com/b/cyrusn/archive/2005/08/01/446431.aspx

一个稍微简单的版本在我的博客上。

http://blogs.msdn.com/b/ericlippert/archive/2007/07/27/an-inheritance-puzzle-part-one.aspx

我版本的解决方案在此处:

http://blogs.msdn.com/b/ericlippert/archive/2007/07/30/an-inheritance-puzzle-part-two.aspx

简而言之,混乱行为的原因是当你有一个既存在于外部类中又存在于基类中的名称时,基类“获胜”。也就是说,如果你有:

public class B
{
  public class X {}
} 
public class P
{
  public class X
  {
    public class D : B
    {
      public class N : X {}
    }
  }
}

那么P.X.D.N继承自B.X,而不是P.X。这个谜题以嵌套泛型类型的方式构建,因此相同的声明可以通过“外部”和“基础”搜索路径进行命名,但由于泛型构造而在每个路径中具有不同的含义。

无论如何,请阅读博客文章上的解释,如果仍然不清楚,请提出更具体的问题。


8

好的,我的第一个答案是错误的。嵌套很重要:

o.b.GetType()中,b是周围类的成员,该类被实例化为B<char>,它继承自A<char>,进而使T1等于char。 以下内容不太清楚(对于A_int.B_char.C_bool的手动实例化):

public class A_bool
{
    public bool a;

    public class B_bool : A_bool
    {
        public bool b;
    }
}

public class A_char
{
    public char a;

    public class B_bool : A_bool
    {
        public char b;
    }
}

public class A_int
{
    public int a;

    public class B_char : A_char
    {
        public int b;

        public class C_bool : A_char.B_bool
        {
            public int c;
        }
    }
}

这种情况下,C_bool 也可以从 A_bool.B_bool 派生,对吗?但是由于我们嵌套在 A_char 中,所以更倾向于选择这个。


1
那么为什么o.c不是bool类型?因为C<bool>继承自B<bool>,而B<bool>又继承自A<bool>。 - Memet Olsen
1
是的,令人困惑的是继承和封闭类型之间的区别。C<bool>B<char>封闭,但从B<bool>继承。 - CubeSchrauber
这是一个很好的方式来说明情况! - Eric Lippert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接