继承和泛型类型设置

时间:2016-12-25 09:21:15

标签: c# generics inheritance

有人可以向我解释为什么下面的代码会输出它的作用吗? 为什么T是一个字符串在第一个,而不是Int32,为什么它在下一个输出中是相反的情况?

这个谜题来自interview with Eric Lippert

当我查看代码时,我真的不知道它是否会成为Int32或字符串:

public class A<T>
    {
        public class B : A<int>
        {
            public void M() { System.Console.WriteLine(typeof(T)); }
            public class C : B { }
        }
    }

    public class P
    {
        public static void Main()
        {            
            (new A<string>.B()).M(); //Outputs System.String
            (new A<string>.B.C()).M(); //Outputs System.Int32
            Console.Read();
        }
    }

5 个答案:

答案 0 :(得分:5)

  

有人可以向我解释为什么以下代码会输出它的作用吗?

问题的关键在于确定<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape android:shape="rectangle"> <gradient android:angle="270" android:endColor="#000000" android:startColor="#FFFFFF" /> <corners android:radius="10dp" /> <padding android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp" /> </shape> </item> <item> <shape android:shape="rectangle"> <solid android:color="#e95d11" /> <corners android:radius="10dp" /> <size android:width="50dp" android:height="50dp" /> </shape> </item> </layer-list> B的含义。考虑一个没有泛型的版本:(为简洁起见,我将省略公众。)

class C : B

可能是class D { class E {} } class J { class E {} class K : D { E e; // Fully qualify this type } } J.E;这是什么?解析名称时C#中的规则是查看基类层次结构,只有当它失败时,才能查看容器。 K已经通过继承拥有成员E,因此不需要查看其容器以发现其容器具有包含成员E.

但是我们看到这个拼图具有相同的结构;它只是被仿制药混淆了。我们可以像处理模板那样处理泛型,只需将A-of-string和A-of-int的结构写成类:

D.E

现在应该清楚为什么class A_of_int { class B : A_of_int { void M() { Write("int"); } class C : B { } // A_of_int.B } } class A_of_string { class B : A_of_int { void M() { Write("string"); } class C : B {} // still A_of_int.B } } A_of_string.B.M()stringA_of_string.B.C.M()

答案 1 :(得分:4)

M内的方法B打印typeof(T)的{​​{1}},A<T>A的父类。

因此无论B是否来自任何内容,B打印M都是typeof(T)

因此String会打印最接近A<T>.B.M的{​​{1}}。

因此A将打印T

现在,让我们展开表达式A<string>.B.M,它相当于string(因为A<string>.B.CA<string>.B.A<int>.B),所以方法C会打印最近的{ {1}}。

A<int>.B将打印A<string>.B.A<int>.B.M

答案 2 :(得分:3)

Introduction to Generics T也可以在嵌套类中使用。类B的情况就是这种情况,它嵌套在A中。另一方面,C嵌套在B中,B的T可用于C。如您所见,B的T为intC上调用的方法将使用int作为通用参数。

答案 3 :(得分:3)

方法M()始终打印其类的父类的泛型参数的类型:

因此(new A<string>.B.C()).M();应打印B的通用类型,始终为int。 (您可以看到B始终为A<int>

另外(new A<string>.B()).M();应打印string,因为B的父级是A<string>

答案 4 :(得分:2)

稍微更改代码:

public class A<T>
{
    public class B : A<int>
    {
        public void M() { System.Console.WriteLine(typeof(T)); }
        public class C : A<T>.B { }
    }
}

public class P
{
    public static void Main()
    {            
        (new A<string>.B.C()).M(); //Outputs System.String
    }
}

请注意我如何将C的基类从B更改为A<T>.B。这会将输出从System.Int32更改为System.String

如果不这样做,A<string>.B.C不是来自A<string>.B,而是来自A<int>.B,导致您已经看到的行为。这是因为通常,基类中定义的名称可通过非限定查找获得,名称B在基类A<int>中定义。