c# 如何处理嵌套(泛型)类型?

How does c# handle nested (generic) types?

我正在尝试了解 C# 在面对嵌套时如何看待类型。
更具体地说,当 "kind of" 仅存在嵌套 class 的一个定义时,我试图理解为什么某些类型不被视为赋值兼容甚至不可转换。编译器/CLR 是否真的为这些生成不同的类型,或者究竟是什么规则在起作用...

示例代码:

public class Foo<T>
{
    protected class Private2 : Private1<Foo<T>>
    { }

    protected class Private1<T2> where T2 : Foo<T>
    {
        public sealed class Nested
        {
            public void Test(T2 foo)
            {
                foo.Method2(this); //Nope!
                var nes = (Private2.Nested)this; //Nope!
            }
        }
    }

    public void Method1()
    {
        var nested = new Private2.Nested();
        nested.Test(this);
    }

    private void Method2(Private2.Nested nested)
    {
        // something code...
    }
}

因此,即使嵌套实例创建为 Private2.Nested,也无法转换为该类型。而且......好吧......鉴于 Nested 实际上是密封的,不同的 Nested 类型如何相互关联? (它们不能相互继承,对吗?但另一方面,它们的实现应该 100% 相同……我错了吗?)

主要问题:编译器在 "compiles" 嵌套 class 时究竟在做什么?实际生成了多少个唯一类型(不包括与值类型相关的类型),如果都是 "same" 类型,限制是人为的(因为不安全的转换实际上不会起作用)? (我的意思是所有这些类型的 IL 来自相同的代码定义——所以在某种程度上编译器必须知道。除了它们的类型名称之外,这些类型的实例是否不完全相同?)


第二个问题: 不是我在这里真正要问的,主要是为了简洁/上下文:是否有一些简单的更改可以使上述工作正常进行?我错过了一些明显的东西吗?

绝不能在 Private1<T2> 中直接引用类型 Foo<T> - 只允许使用 T2Foo<T> 只是我的示例,代表具有 10~20 个泛型类型的讨厌的泛型 classes。这只是一个 "workaround" 因为不能用它的类型别名通用 class:

public class Bar<GoodName, OtherName, Readability, NopeMr, DontThinkSo, Suffering, Dispair>
{
    //If only this was real...
    using BarT = Bar<GoodName, OtherName, Readability, NopeMr, DontThinkSo, Suffering, Dispair>;

    public void Method1(BarT bar) { ... } //so good!!

    //goodbye readability... see you never...
    public void Method2(Bar<GoodName, OtherName, Readability, NopeMr, DontThinkSo, Suffering, Dispair> whatIsThisVariable) { ... }
}

目的:避免字段和方法参数的类型有几个屏幕宽并且完全不可读! >:(

...作为旁注,我真的希望 this 可以用作 classes 和接口中的类型,如 Private2 : Private1<this>。好吧,那是行不通的,因为它与方法上的扩展语法冲突,但类似的东西,也许 <this><super><base>Method(<this> arg) 或 [=22] =] ...可能有点奇怪。

你没有考虑门户。你内心的类已经在T上泛化了

public class Foo<T>
{
    private class Private2 : Private1
    { }

    private class Private1
    {
        public sealed class Nested
        {
            public void Test( Foo<T> foo )
            {
                foo.Method2( this ); //Yup
                var nes = (Private2.Nested)this; //Yup
            }
        }
    }

    public void Method1()
    {
        var nested = new Private2.Nested();
        nested.Test( this );
    }

    private void Method2( Private2.Nested nested )
    {
        // something code...
    }
}

考虑以下类型:

public class Base {
    public static int Value;
    public class Nested { }
}
public class Derived:Base { }

什么是 Derived.ValueDerived.Nested。实际上,当您通过派生 class 引用继承的静态成员(嵌套 class 被认为是静态成员)时,您只是引用基础 class 成员,因此这与 Base.ValueBase.Nested 在编译时。没有单独的静态字段 Derived.Value 或单独的 class Derived.Nested.

public static void Test() {
    Derived.Value=10;
    Console.WriteLine(Base.Value);
    Base.Value=20;
    Console.WriteLine(Derived.Value);
    Base.Nested bn=new Derived.Nested();
    Derived.Nested dn=new Base.Nested();
    Console.WriteLine(typeof(Base.Nested).FullName);
    Console.WriteLine(typeof(Derived.Nested).FullName);
    Console.WriteLine(typeof(Base.Nested)==typeof(Derived.Nested));
}

原回答:
如果 A!=CB!=D,则 Foo<A>.Private1<B>.NestedFoo<C>.Private1<D>.Nested 被视为不同类型。它们可以在内部共享相同的实现,但是为了分配兼容性它们是不同的。 Foo<T>.Private2.Nested 只是 Foo<T>.Private1<Foo<T>>.Nested 的别名。即使 class Bar:Foo<A>{}、class 和 Foo<A>.Private1<Foo<A>>.NestedFoo<A>.Private1<Bar>.Nested 仍然被认为是不同的类型。所以 Foo<T>.Private1<T2>.Nested 不能转换为 Foo<T>.Private1<Foo<T>>.Nested 因为 T2 不是必需的 Foo<T>.

对主要问题的部分回答:

让我烦恼的是,您可以通过更改 Method2 以接受对象并在运行时转换它来编译代码,因为嵌套实例的类型正确(它在 Method1 中实例化)。这似乎可行——只要 Foo 是密封的——但只要其他人可以将 Private1 子类化,它就不再保证有效。 (因此不是解决方案。)然而,测试这种方法表明:

Private2.Nested is only a construct of syntax rules - using GetType() on the resulting variable says Private1.Nested and there is no Private2.Nested type.

我认为我从中得到的令人讨厌的感觉(以及为什么我认为 sealed 是相关的)是我在区分子类型和继承时的某种混淆。因为外部 类 正在继承(Private1 和 Private2),所以感觉就像是继承,因此感觉它应该以某种方式被转换。但如果我理解正确的话,它们只是相同的子类型:

There need not be and is in fact no inheritance relation one way or the other (as the sealed clearly hints) because "the inheritance hierarchy is distinct from from the subtype hierarchy", and thus a downright conversion would be needed (since casts are bound to the inheritance hierarchy).