【问题标题】:typeof(T) within generic nested types泛型嵌套类型中的 typeof(T)
【发布时间】:2014-01-16 19:31:52
【问题描述】:

我不明白为什么下面的行为会如此。也不知道是隐藏还是其他原因。

class A<T>
{

    public class B : A<int>
    {
        public void b()
        {
            Console.WriteLine(typeof(T).ToString());
        }
        public class C : B
        {
            public void c()
            {
                Console.WriteLine(typeof(T).ToString());
            }
        }
        public class D : A<T>.B
        {
            public void d()
            {
                Console.WriteLine(typeof(T).ToString());
            }
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        A<string>.B.C c = new A<string>.B.C();
        A<string>.B.D d = new A<string>.B.D();
        c.c();
        c.b();
        d.d();
        d.b();
    }
}

问题是:

  1. 为什么c.c() 产生System.String 而c.b() 产生System.Int32

  2. 为什么d.d()d.b() 都产生System.String 并且与C 类的行为方式不同?

【问题讨论】:

  • 似乎这段代码被故意混淆了。解决办法是一开始就不要写这样的代码。
  • 如果有人有正当理由在任何实际代码(无论是否生成)中使用这种乱七八糟的嵌套类,我都会感到惊讶。
  • 实用与否,我目前很困惑 :( 个人认为这类问题非常适合提高人们阅读规范的能力,这本身很有用。
  • 乔恩·斯基特难住了??问题:赞成。

标签: c# generics typeof type-parameter


【解决方案1】:

这是我多年前在博客上发布的一个谜题的变体:

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

赛勒斯此前曾在他的博客上发帖:

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

详情请参阅那里的讨论。

简单地说:Bclass C : B 中是什么意思?检查容器,class B。它是否包含任何名为B 的类型?不,然后检查容器的基类。容器的基类是A&lt;int&gt;。它是否包含任何名为B 的内容?是的。所以这意味着class C : A&lt;int&gt;.B

现在我们说cA&lt;string&gt;.B.C。我们称方法A&lt;string&gt;.B.C.c() 贯穿A&lt;string&gt; 是什么T?显然string。所以c.c() 打印StringT

现在我们调用A&lt;string&gt;.B.C.b(),但A&lt;string&gt;.B.C 中没有直接这样的方法。它从哪里得到这个方法?从它的基类。它的基类是什么? A&lt;int&gt;.B。所以我们打电话给A&lt;int&gt;.B.b()A&lt;int&gt; 中的 T 是什么?显然是int

现在我们来到A&lt;string&gt;.B.D.d()。基类无关紧要。 Tstring 贯穿 A&lt;string&gt;

最后是A&lt;string&gt;.B.D.b()A&lt;string&gt;.B.D 上没有直接这样的方法,所以它必须从它的基类型中获取它。 T 在整个A&lt;string&gt; 中都是string,所以基本类型是A&lt;string&gt;.B。因此这调用A&lt;string&gt;.B.b()

如果这对您没有意义,请将所有内容拼写出来。让我们用 String 代替 T:

class A_string
{
    public class B : A_int
    {
        public void b()
        {
            Console.WriteLine(typeof(string).ToString());
        }
        public class C : A_int.B // Note!
        {
            public void c()
            {
                Console.WriteLine(typeof(string).ToString());
            }
        }
        public class D : A_string.B
        {
            public void d()
            {
                Console.WriteLine(typeof(string).ToString());
            }
        }
    }
}

好的,这是其中一种类型。现在让我们对 int 做同样的事情:

class A_int
{
    public class B : A_int
    {
        public void b()
        {
            Console.WriteLine(typeof(int).ToString());
        }
        public class C : A_int.B // Note!
        {
            public void c()
            {
                Console.WriteLine(typeof(int).ToString());
            }
        }
        public class D : A_int.B
        {
            public void d()
            {
                Console.WriteLine(typeof(int).ToString());
            }
        }
    }
}

现在给定这些类型,应该清楚A_string.B.C.c()A_string.B.C.b() 等都打印出来了。

【讨论】:

  • 而且——这也是因为我们先在基类中查找方法,然后再转到外部类,对吧?
  • @PetrHudeček:我们先在基类中查找names,然后再查找外部类。
  • @PetrHudeček:不是方法,而是基本类型。
  • @SLaks:你什么意思?我的意思是......当我们在 Main 方法中寻找 c.b() 时,其中 c 为 A.B.C,但我们在类本身中找不到方法 b,那么我们可以查看它的基类类(即 A.B)或其外部类(即 A.B)。我理解,编译器更喜欢查看基类。
  • @PetrHudeček:错了。永远不会从外部类中查找实例成员;毕竟,您实际上没有外部类的实例来调用它。这里的问题是关于基声明中的 type 查找; B 是指A&lt;int&gt;.B(从基类型的外部类继承为A&lt;string&gt;.B.B)还是A&lt;string&gt;.B(从最外部类继承)
【解决方案2】:

A&lt;string&gt;.B.C继承A&lt;int&gt;.B,因为基类声明中的B首先来自内部父作用域。 (澄清一下,它的父作用域是A&lt;T&gt;.B,其中包含一个名为B的类型,引用A&lt;int&gt;.B,继承自其基类A&lt;int&gt;

调用b()来自其基类,其中T(来自父作用域)是int

D 显式继承A&lt;T&gt;.B,使用来自最外层范围(A&lt;T&gt;)的T,因此其T 始终来自其类型名中的A&lt;&gt;

【讨论】:

  • 最近的编辑解释了它。这确实让我想知道其他 T 是否可以通过某种方式访问​​。
  • @Magus:在B的基类方法中,没有其他类型;泛型类型参数不是多态的。
  • c.GetType()A`1+B+C[System.String];它的BaseTypeA`1+B[System.Int32]
  • 是 D 让我感到困惑。它必须是 A.B,对吧?但是 int 不可访问?
  • @MagusL No. B 不是通用的。派生类不继承类型参数。 T 内的 B 指的是来自父作用域的 A&lt;T&gt; 中的 T。第一个不同,因为它实际上是A&lt;int&gt;.B
【解决方案3】:

这是 Eric Lippert 在this blog article 中描述的一个非常复杂的谜题示例,随后在this article 中进行了解释。

简短的总结(我强烈建议您只阅读这篇文章)是在这种情况下,C : B 行存在歧义。需要决定B 在这种情况下的实际含义;无论是A&lt;T&gt;.B 还是A&lt;int&gt;.B。本质上,编译器会根据规范中描述的特定“更好”标准选择后者。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    • 2019-03-10
    • 1970-01-01
    • 2023-01-10
    相关资源
    最近更新 更多