Polymorphism by inheritance and Shadows (conflict?)

L

Lino Barreca

Take a look at this code:

Class clsAnagrafica
Public Overridable ReadOnly Property Codice() As Integer
Get
Return 1
End Get
End Property
End Class

Class clsCliente
Inherits clsAnagrafica

Public Overrides ReadOnly Property Codice() As Integer
Get
Return 2
End Get
End Property
End Class

Class clsDocumento
Public Anagrafica As New clsAnagrafica
End Class

Class clsDocumentoCliente
Inherits clsDocumento

Public Shadows Anagrafica As New clsCliente
End Class

Module Module1
Sub Main()
Dim A As clsAnagrafica
A = New clsCliente
Debug.WriteLine(A.Codice)

Dim D As clsDocumento
D = New clsDocumentoCliente
Debug.WriteLine(D.GetType)
Debug.WriteLine(D.Anagrafica.Codice)
End Sub
End Module

A.Codice returns 2 (because of Overridden member).

D.GetType obviously returns clsDocumentoCliente.
But the strange thing is that D.Anagrafica.Codice returns 1.

I can't understand why!
clsDocumentoCliente.Anagrafica shadows clsDocumento.Anagrafica
so the compiler should call clsDocumentoCliente.Anagrafica.Codice. (as MSDN
states "the same base method can perform different actions depending on the
run-time type of the instance that invokes the method") and the run-time
type is clsDocumentoCliente.

However I find that this behaviour is somewhat related to the initial
declaration of D.
Infact if I declare D as object D.Anagrafica.Codice returns 2. (as it should
be)

Can anyone explain me why this happens?

Thanks in advance, Lino.
 
R

Rogelio Moreno

Lino,

Remember that when a member has been shadowed, no
inheritance relationship exists between the two members
and therefore you access the member in the base class.
(take a look to the Francesco Balena book:programming
Visual Basic .Net, page 259)

btw, this is an excellent book!

Rogelio
-----Original Message-----
Take a look at this code:

Class clsAnagrafica
Public Overridable ReadOnly Property Codice() As Integer
Get
Return 1
End Get
End Property
End Class

Class clsCliente
Inherits clsAnagrafica

Public Overrides ReadOnly Property Codice() As Integer
Get
Return 2
End Get
End Property
End Class

Class clsDocumento
Public Anagrafica As New clsAnagrafica
End Class

Class clsDocumentoCliente
Inherits clsDocumento

Public Shadows Anagrafica As New clsCliente
End Class

Module Module1
Sub Main()
Dim A As clsAnagrafica
A = New clsCliente
Debug.WriteLine(A.Codice)

Dim D As clsDocumento
D = New clsDocumentoCliente
Debug.WriteLine(D.GetType)
Debug.WriteLine(D.Anagrafica.Codice)
End Sub
End Module

A.Codice returns 2 (because of Overridden member).

D.GetType obviously returns clsDocumentoCliente.
But the strange thing is that D.Anagrafica.Codice returns 1.

I can't understand why!
clsDocumentoCliente.Anagrafica shadows clsDocumento.Anagrafica
so the compiler should call
clsDocumentoCliente.Anagrafica.Codice. (as MSDN
 
M

Mattias Sjögren

Lino,
I can't understand why!
clsDocumentoCliente.Anagrafica shadows clsDocumento.Anagrafica
so the compiler should call clsDocumentoCliente.Anagrafica.Codice. (as MSDN
states "the same base method can perform different actions depending on the
run-time type of the instance that invokes the method") and the run-time
type is clsDocumentoCliente.

Two things:

- Field access is never polymorphic, methods calls can be. Anagrafica
is a field.
- Using Shadows *breaks* polymorphism - that's its whole purpose. If
you want polymorphism, you should use Overridable/Overrides instead.



Mattias
 
F

Fergus Cooney

Hi Lino,

A challenging question this one!! Thank you Lino :)

There's so much potential for confusion with overloading, shadowing,
overriding, etc. It's even worse when you learn the C# and VB versions -
totally different names for all these things and some subtle differences
thrown in. Mind boggling. I'm still studying this area but here's what I've
got for you.

|| Dim D As clsDocumento
|| D = New clsDocumentoCliente

This is the key part.

D is a clsDocumento.
It's <not> a clsDocumentoCliente.
It's a clsDocumento that <holds> a clsDocumentoCliente.

The compiler knows this, and thus, when you access the members of D, it's
going to access them according to what <D> is - not according to what it
<holds>. [Except - see Overrides below]

So.
D.Anagrafica.Codice
refers to the Anagrafica of clsDocumento which is the
Public Anagrafica As New clsAnagrafica

If you declare D as Object, then the compiler says "I don't know what this
is - let's sort it out at runtime".

"So what about polymorphism?", I hear you cry plaintively.

This is where the difference between Shadows and Overrides comes in.

"Shadows" says, in effect, start with what the <object variable> is and
find a member by checking the inheritance list. Shadows is not intended for
polymorphism.

"Overrides" says, in effect, "start with what the object variable
<contains> and find a member by checking the inheritance list. Overrides is
the keyword for polymorphism.

I've attached a project which shows you the differences between Shadows
and Overrides. It defines a hierarchy of five classes. Each of the classes is
assigned to an Object and to a variable of each of the other types in the
hierarchy (as far as allowed). Then the methods are called and the results
shown. A bit like your example but taken further.

Have a go of it and give it a bit of a study. It should show you the
pattern.

All the best,
Fergus
 
L

Lino Barreca

Two things:
- Field access is never polymorphic, methods calls can be. Anagrafica
is a field.

I know that polymorphism doesn't apply to fields.
The same thing happens if i declare:

private m_Anagrafica as clsAnagrafica
public overridable property Anagrafica as clsAnagrafica
get
return m_Anagrafica
end get
end property

private m_Anagrafica as clsCliente
public SHADOWS property Anagrafica as clsCliente
get
return m_Anagrafica
end get
end property
- Using Shadows *breaks* polymorphism - that's its whole purpose. If
you want polymorphism, you should use Overridable/Overrides instead.

I CAN'T use Overridable/Overrides! :)
Because clsDocumento.Anagrafica is clsAnagrafica
and clsDocumentoCliente.Anagrafica is clsCliente. And Overridable/overrides
can't be used if the types are different.
The only way (I know) is to shadow base class' member.

Any ideas?
 
L

Lino Barreca

First of all let me thank you.

I've read what you wrote and it makes sense :) but the question is: how can
I solve the problem?

Probably you'll say "declare D directly as clsDocumentoCliente" :)

I can't..my problem is a little bit more complicated:
D is a parameter of a sub that can, should and must accept any object that
derives from clsDocumento

public sub DoSomething(D as clsDocumento)
....
end sub

I could declare D as object disabling the option strict or I could leave the
parameter as it is and test each possible type of D and make the appropriate
Ctype() before calling ANY method of it.

but theese workarounds seem to me one worst than other.

Do you have any ideas?
thanks, Lino
 
F

Fergus Cooney

Hi Lino,

Perhaps if you tell us more about what you are trying to achieve...

Regards,
Fergus
 
F

Fergus Cooney

Hi Lino,

|| Probably you'll say "declare D directly as clsDocumentoCliente" :)

Wouldn't dream of it. :) Having the base class as the parameter is the
whole point!


What's been confusing me is why you have the clsAnagrafica as a Public
field. That's why I asked (in the other branch of the thread) for more info
about what you are doing.

Anyway, then I got to read your post above, and had a play around.

As we've discovered, Shadows is all that you can use on fields. Overrides
is what you need for polymorphism, but it has to be applied to Functions and
Subs.

So I came up with this: I've no idea if it's what you're after but it
<does> call clsCliente.Codice() when D is a clsDocumento containing a
clsDocumentoCliente.

Class clsDocumento
Public m_Anagrafica As New clsAnagrafica
Public Overridable Function Anagrafica As clsAnagrafica
Return m_Anagrafica
End Function
End Class

Class clsDocumentoCliente : Inherits clsDocumento
Public Shadows m_Anagrafica As New clsCliente
Public Overrides Function Anagrafica As clsAnagrafica
Return m_Anagrafica
End Function
End Class

Come back if we need to do some more thinking on this. :)

[Out of curiousity, what does "anagrafica" means ?]

Regards,
Fergus
 
R

Rogelio Moreno

Lino,


You can declare Anagrafica property as "Object" and
return "New clsAnagrafica" or "New clsCliente" this
way you can do the Overrridable and Overrrides.

Rogelio
-----Original Message-----
Take a look at this code:

Class clsAnagrafica
Public Overridable ReadOnly Property Codice() As Integer
Get
Return 1
End Get
End Property
End Class

Class clsCliente
Inherits clsAnagrafica

Public Overrides ReadOnly Property Codice() As Integer
Get
Return 2
End Get
End Property
End Class

Class clsDocumento
Public Anagrafica As New clsAnagrafica
End Class

Class clsDocumentoCliente
Inherits clsDocumento

Public Shadows Anagrafica As New clsCliente
End Class

Module Module1
Sub Main()
Dim A As clsAnagrafica
A = New clsCliente
Debug.WriteLine(A.Codice)

Dim D As clsDocumento
D = New clsDocumentoCliente
Debug.WriteLine(D.GetType)
Debug.WriteLine(D.Anagrafica.Codice)
End Sub
End Module

A.Codice returns 2 (because of Overridden member).

D.GetType obviously returns clsDocumentoCliente.
But the strange thing is that D.Anagrafica.Codice returns 1.

I can't understand why!
clsDocumentoCliente.Anagrafica shadows clsDocumento.Anagrafica
so the compiler should call
clsDocumentoCliente.Anagrafica.Codice. (as MSDN
 
L

Lino Barreca

I want to fully explain what I've to do.

I have a WinForm (ok..it's not only one..but think of it as one).
This form is used to compile a "document" (clsDocumento)
A "document" (I'm simplifying) basically is an array of products/services
with a total value and an ID of a person assigned to it
(clsAnagrafica.Codice= clsPerson.ID)

clsAnagrafica holds many infos related to a generic person.
clsCliente extends and specializes clsAnagrafica with customer related
informations (ie: preferred payment method, VAT info, predefined discounts,
and so on..)
clsFornitore (not mentioned) extends and specializes clsAnagrafica and holds
the infos for the supplier/producer for a given set of products.
clsVettore (not mentioned) extends and specializes clsAnagrafica and holds
the infos for the carrier that must physically carry the products from a
location to another.

However there are a lot of different types of documents.
Take for example an invoice to a customer (clsDocumentoCliente) it derives
from a generic document with some particular extentions (billing and
shipping information, discounts, offers, payment infos and so on).
clsDocumentoFornitore is a invoice of purchase.
clsDocumentoVettore is a bill for a carrier.

my WinForm enables/disables some tabpages (billing, shipping...) according
to type of D (the supplier of a product uses different sums, calculations,
infos and so on. for example: an invoice of purchase from a supplier means ?
that go out and invoice to a customer are ? that come in)

Stated it...

The code that I actually use is D.Anagrafica.Codice=(id of
customer/producer/carrier and so on)
the Property Set Codice loads from a DB all the infos related to that type
of Anagrafica.

clsAnagrafica.Codice_set() does:
load the generic info (name, surname, title and so on) from tblAnagrafica
and store them in clsAnagrafica's fields.

clsCliente.Codice_set() does:
load customer specific infos from tblCliente and store it in clsCliente's
fields.
mybase.Codice=value

In this way I'd like to obtain clsDocumentoCliente.Anagrafica.Name (stored
in clsAnagrafica)
and clsDocumentoCliente.Anagrafica.Addresses.Billing.City (stored in
clsCliente..not really..but assume it's true)

Unfortunately D.Anagrafica.Codice=someid calls the
clsAnagrafica.Codice_set(). This leaves all the clsCliente's fields at their
default empty values.

I've already thought to declare
Class clsDocumentoCliente : Inherits clsDocumento
Public Shadows m_Anagrafica As New clsCliente
Public Overrides Function Anagrafica As clsAnagrafica
Return m_Anagrafica
End Function
End Class
(not with a function but with a property, however the concept is the same).
The polymorphism works (obviously) but this it not pratical because there're
a lot of procedures (related to clsDocumentoCliente) that accept a (DC as
clsDocumentoCliente)
and call many DC.Anagrafica.MethodThatExistsOnlyInCLSCliente.
Changing the type returned by property/function forces me to alter
everywhere: CType(DC.Anagrafica,
clsCliente).MethodThatExistsOnlyInCLSCliente
And this is not pratical. :)

Actually I used this workaround:
Class clsDocumentoCliente : Inherits clsDocumento
Public Shadows m_Anagrafica As New clsCliente

public sub new()
mybase.Anagrafica=m_anagrafica
end sub
.....
end class

This pushes a clsCliente object into clsDocumento.clsAnagrafica..
Doing this I can obtain what I want because clsDocumentoCliente.Anagrafica
is forced to be a clsCliente and the clsDocumentoCliente.Anagrafica.Codice
calls clsCliente.Codice_set() and the subsequent clsCliente.Load.

I'm still searching for a bug...I don't know if this workaround can cause
some strange behaviour.

if you have a better idea every suggestion is well accepted.
Many thanks for your help.
Lino.
 
F

Fergus Cooney

Hi Lino,

Thanks for the detailed info about your use of these classes. I'm going to
ask for more. :)

I'm having trouble putting together something concrete that I can actually
play with and study.

What would be very useful is the definitions of all the classes that are
involved and examples of how you are calling them with the different types of
reference variable, eg D As Doc = DC, etc. Examples of calling functions where
you have one or the other or both types, etc. Examples of etc, etc - whatever
you can give me.

Code that you are actually using - not imaginary or wishful. But cut-down,
however, so that there's not too much irrelevant stuff to push aside.

This might be more effort than it's worth to you. No problem - I'll
understand. :)

In the meantime I'm going to try and work something from all that you've
told me so far. It looks as if it'll take a good hard think. [Scratches head
and wanders off, muttering to himself, "hmmm, now, if I just...."]

Catch you later,

Regards,
Fergus
 

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