Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does c# handle nested (generic) types?

Tags:

c#

types

nested

I'm trying to understand how C# views types in the face of nesting.
More specifically I'm trying to understand why some types are not considered assignment compatible or even castable, when there "kind of" only exist one definition of the nested class. Does the compiler / CLR actually generate different types for these, or what rules are at play exactly...

Example code:

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...
    }
}

So even though the nested instance is created as a Private2.Nested it can not be casted to that type. And... well... how do the different Nested types relate to each other given that Nested is in fact sealed? (They can't be inheriting from each other right? But on the other hand their implementation should be 100% identical... am I wrong?)

Primary question: What exactly is the compiler doing when it "compiles" this nested class?? How many unique types (excluding valuetype-related) are actually generated, and if it is all the "same" type, is the restriction artificial (as in wouldn't an unsafe cast actually work)? (What I'm saying is that the IL for all these types comes from the same code definition - so at some level the compiler must know. Are instances of these types not bit-for-bit identical apart from their type-names?)


Secondary question: not what I'm really asking here, mostly for brevity / context: is there some simple change that would make the above work? Am I missing something obvious?

The type Foo<T> must never be directly referenced inside Private1<T2> - only use of T2 is allowed. Foo<T> is just my example stand in for nasty generic classes with 10~20 generic types. It's all just a "workaround" for not being able to alias a generic class with its types:

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) { ... }
}

Purpose: To avoid types of fields and method-parameters that are several screens wide and utterly unreadable! >:(

...As a side note I really wished this could be used as a type inside classes and interfaces, as in Private2 : Private1<this>. Well ok, that wouldn't work because it collides with extension syntax on methods, but something similar, perhaps <this>, <super>, <base> used like Method(<this> arg) or Private2 : Private1<<super>> ... kind of weird maybe.

like image 593
AnorZaken Avatar asked Feb 27 '15 21:02

AnorZaken


People also ask

How does the C language work?

C is what's referred to as a compiled language, meaning you have to use a compiler to turn the code into an executable file before you can run it. The code is written into one or more text files, which you can open, read and edit in any text editor, such as Notepad in Windows, TextEdit on a Mac, and gedit in Linux.

How is C language made?

The C language was actually created to move the UNIX kernel code from assembly to a higher level language, which would do the same tasks with fewer lines of code. Oracle database development started in 1977, and its code was rewritten from assembly to C in 1983. It became one of the most popular databases in the world.

Is C hard to write?

It is hard to learn because: It has complex syntax to support versatility. It is a permissive language—you can do everything that's technically possible, even if not logically right. It is best learned by someone who already has a foundation with C programming.


2 Answers

Consider this types:

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

What is Derived.Value and Derived.Nested. Actually, when you refer to inherited static members (nested class considered to be static member) thru derived class, you just reference base class members, so this have exactly same meaning as Base.Value and Base.Nested at compile time. There are no separate static field Derived.Value or separate 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));
}

Original answer:
Foo<A>.Private1<B>.Nested and Foo<C>.Private1<D>.Nested considered to be different types if A!=C or B!=D. They can share same implementation internally, but for assignment compatibility they are different. Foo<T>.Private2.Nested is just alias to Foo<T>.Private1<Foo<T>>.Nested. And even if class Bar:Foo<A>{}, classes Foo<A>.Private1<Foo<A>>.Nested and Foo<A>.Private1<Bar>.Nested still considered to be different types. So Foo<T>.Private1<T2>.Nested can not be converted to Foo<T>.Private1<Foo<T>>.Nested as T2 is not necessary Foo<T>.

like image 145
user4003407 Avatar answered Nov 15 '22 13:11

user4003407


You're not thinking with portals. Your inner classes are already generalized on 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...
    }
}
like image 38
antiduh Avatar answered Nov 15 '22 12:11

antiduh