Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I have to parent-type-qualify to use a nested type when inheriting from a generic type?

Tags:

c#

Consider this:

[SomeAttr(typeof(Bar))]
class Foo {

   class Bar {

   }
}

...and this:

class Foo : ISomething<Bar> {

   class Bar {

   }
}

The first example compiles, the second doesn't. To use a nested type reference when inheriting from a generic type I must qualify it with the parent's type name:

class Foo : ISomething<Foo.Bar> {

   class Bar {

   }
}

My question is, why? and why doesn't this restriction apply when referring from an attribute in the parent type (first example)?

like image 740
Max Toro Avatar asked Apr 04 '14 01:04

Max Toro


1 Answers

It's all about the scope of the declaration. Consider the following code:

namespace FooSpace
{
    class Foo : ISomething<Bar> 
    {
        class Bar { }
    }
 }

The line of code class Foo : ISomething<Bar> is in the FooSpace namespace, but not inside the class Foo. At this point, Bar has no meaning. Bar by itself would only have a meaning within the scope of the Foo class itself.

To use the class Bar in any code outside the Foo class, you have to qualify it as Foo.Bar. This rule also applies within the class declaration itself.

Why is it this way? Couldn't the complier automatically work out that you must mean the nested Bar within the same class?

Well, for one thing there could potentially be other nested classes called Bar within other classes in the same namespace, as follows:

namespace FooSpace
{
    class Foo : List<Foo.Bar>
    {
        public class Bar { }
    }

    class Foo2 : List<Foo2.Bar>
    {
        public class Bar { }
    }
}

Without the qualifier, which Bar do you mean? The compiler would have no way to tell. So it's much more consistent for the compiler not to try and guess, but to insist on an explictly qualified class name.

As for the attribute case, although a class-targetting attribute is declared above the class declaration like this:

 [SomeAttr(typeof(Bar))]
 class Foo { ...

in reality, the compiler translates this to something more like this:

class public auto ansi beforefieldinit FooSpace.Foo extends [mscorlib]System.Object
{
    .custom instance void FooSpace.SomeAttr::.ctor ... etc.

In other words, the actual SomeAttr attribute object is created inside the class, using the parameter you supply. This parameter typeof(Bar) is of course then valid, as it exists within the class scope of Foo.

The related question here discusses this case too.

It's interesting to note that exactly the same rules and behaviours apply to class attributes which access const or static class members - you don't need the specify the class name, because the attribute is actually created inside the class.

like image 111
Baldrick Avatar answered Sep 19 '22 14:09

Baldrick