Mixing abstract and template inheritance

I

Israel

Can someone explain to me why I get a compiler error with the
following code on the line obj.DoSomething()?
but if cast the object I get no error. The error I get is:
Cannot access protected member
'GenericDerivedClassTest.Base.DoSomething()' via a qualifier of type
'GenericDerivedClassTest.Base'; the qualifier must be of type
'GenericDerivedClassTest.TBase<DerivedType>' (or derived from it)

Why does it believe the object has to derive from TBase<DerivedType>?
when all it needs to do is derive from Base since that's where the
method is and that's also what TBase<DerivedType> derives from.

It seems to me that the compiler is confused because generics are kind
of like templates/macros and maybe doesn't know how to really traverse
the inheritance hierarchy in all cases?

here's the code:
/// <summary>
/// Templated absract class
/// </summary>
/// <typeparam name="DerivedType"></typeparam>
public abstract class TBase<DerivedType> : Base
where DerivedType : TBase<DerivedType>, new()
{
public TBase()
{
}

public static DerivedType StaticDoSomething()
{
// Create a new derived class type
Base obj = (Base)(new DerivedType());

// Let the base class first do something to the object
StaticDoSomethingImpl(ref obj);

// Call the derived class
#if true // This yields the error "Cannot access protected
member..."
obj.DoSomething();
#else // This works fine
TBase<DerivedType> obj2 = (TBase<DerivedType>)obj;
obj2.DoSomething();
#endif

return (DerivedType)obj;
}
}

/// <summary>
/// Abstract base class
/// </summary>
public abstract class Base
{
public Base()
{
}

public static void StaticDoSomethingImpl(ref Base obj)
{
// Do something to obj
}

protected abstract void DoSomething();
}
 
J

jmDesktop

Can someone explain to me why I get a compiler error with the
following code on the line obj.DoSomething()?
but if cast the object I get no error.  The error I get is:
Cannot access protected member
'GenericDerivedClassTest.Base.DoSomething()' via a qualifier of type
'GenericDerivedClassTest.Base'; the qualifier must be of type
'GenericDerivedClassTest.TBase<DerivedType>' (or derived from it)

Why does it believe the object has to derive from TBase<DerivedType>?
when all it needs to do is derive from Base since that's where the
method is and that's also what TBase<DerivedType> derives from.

It seems to me that the compiler is confused because generics are kind
of like templates/macros and maybe doesn't know how to really traverse
the inheritance hierarchy in all cases?

here's the code:
/// <summary>
/// Templated absract class
/// </summary>
/// <typeparam name="DerivedType"></typeparam>
public abstract class TBase<DerivedType> : Base
    where DerivedType : TBase<DerivedType>, new()
{
    public TBase()
    {
    }

    public static DerivedType StaticDoSomething()
    {
        // Create a new derived class type
        Base obj = (Base)(new DerivedType());

        // Let the base class first do something to the object
        StaticDoSomethingImpl(ref obj);

        // Call the derived class
#if true    // This yields the error "Cannot access protected
member..."
        obj.DoSomething();
#else       // This works fine
        TBase<DerivedType> obj2 = (TBase<DerivedType>)obj;
        obj2.DoSomething();
#endif

        return (DerivedType)obj;
    }

}

/// <summary>
/// Abstract base class
/// </summary>
public abstract class Base
{
    public Base()
    {
    }

    public static void StaticDoSomethingImpl(ref Base obj)
    {
        // Do something to obj
    }

    protected abstract void DoSomething();



}- Hide quoted text -

- Show quoted text -

Curious. Why do you have to cast to Base, if your DerivedType is a
Base type? Why this:

Base obj = (Base)(new DerivedType());

Instead of

Base obj = new DerivedType();

Sorry. I know that's not what you asked. I am learning, trying to
understand.
 
J

Jon Skeet [C# MVP]

Can someone explain to me why I get a compiler error with the
following code on the line obj.DoSomething()?

It's worth getting rid of generics from the equation to start with -
they're irrelevant here. Likewise you don't need to have any abstract
classes. The issue is to do with your understanding of the "protected"
access modifier. Here's a simpler example:

class Base
{
protected void Foo() { }
}

class Derived : Base
{
public void CallFoo(Base b)
{
b.Foo();
}
}

class Other : Base {}

You'll see the same error message. "protected" doesn't mean "can be
called by any derived type on any instance of the base class". It
means "can be called by any derived type on any instance *of the same
type* (including instances of further derived types)".

So Derived can call Foo on any Derived, but not on an instance of
"Other".

The C# 3.0 language spec describes it like this in section 3.5.3:
<quote>
When a protected instance member is accessed outside the program text
of the class in which it is declared, and when a protected internal
instance member is accessed outside the program text of the program in
which it is declared, the access must take place within a class
declaration that derives from the class in which it is declared.
Furthermore, the access is required to take place through an instance
of that derived class type or a class type constructed from it. This
restriction prevents one derived class from accessing protected
members of other derived classes, even when the members are inherited
from the same base class.
</quote>

Jon
 
I

Israel

<quote>
When a protected instance member is accessed outside the program text
of the class in which it is declared, and when a protected internal
instance member is accessed outside the program text of the program in
which it is declared, the access must take place within a class
declaration that derives from the class in which it is declared.
Furthermore, the access is required to take place through an instance
of that derived class type or a class type constructed from it. This
restriction prevents one derived class from accessing protected
members of other derived classes, even when the members are inherited
from the same base class.
</quote>

Ok, that makes sense from the example you gave since it's impossible
for the compiler to know whether the passed in object is the base
class from another inheritance branch but you could argue that the
compiler should know this to be impossible in my case since the
creation of the object is within the method and that object is defined
to be derived from the current class that it's in.
Don't get me wrong it's not worth arguing too much because I could
have just kept the original object as a DerivedType type and then
everything would work fine.
 
J

Jon Skeet [C# MVP]

Israel said:
Ok, that makes sense from the example you gave since it's impossible
for the compiler to know whether the passed in object is the base
class from another inheritance branch but you could argue that the
compiler should know this to be impossible in my case since the
creation of the object is within the method and that object is defined
to be derived from the current class that it's in.

You don't want the compiler to start having to make that kind of
inference though - the complexity of the language balloons massively.
Heck, type inference in C# 3 is complicated enough as it is for a
single expression - I don't want to have to wade through rules about
what extra information the compiler is required to take into account.
It would get really hairy.
Don't get me wrong it's not worth arguing too much because I could
have just kept the original object as a DerivedType type and then
everything would work fine.

Exactly. And in cases where you know better than the compiler for
whatever reason, you can always cast. That's what casting's for :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top