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.
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.
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.
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.
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>
.
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...
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With