We help IT Professionals succeed at work.

VB.Net Alternatives to Casting from a superclass to a subclass

1,354 Views
Last Modified: 2012-02-09
Hello experts,

I am working with a vb.net winform and some custom classes.

What i am trying to do:

I have a class that retrieves objects from a DB and overrides the ToString Method
public class myBaseClass

private sub new()
 'made private so that a data row cannot be represented by multiple instances
end sub

 public shared function getInstances as List(of myBaseClass)
...
end function

public overrides function toString() as String
 return "The most normal representation of this object as a string"
end function

end class

Open in new window


I have a form that when it loads attempts to get the data from myBaseClass and display it in a combobox on the form
private sub displayMyBaseClasses()
for each MBC as myBaseClass in myBaseClass.getInstances
  me.mycombobox.items.add(MBC)
next

Open in new window

this works great at getting the objects into the control showing there base toString as the text, and maintaining the myBaseClass object as the selectedItem.(in some cases)

the problem is that for this form I do not want to use the default ToString of the myBaseClass objects.  I would like to use a different Implementation of toString for this class.

I have tried creating a superClass with a new toString Method
public class mySuperClass
   inherits myBaseClass
  public overrides function toString() as String
   return "A Specialized text representation of myBaseClass"
  end function
end class

Open in new window


the thought being that i could cast the objects to this class before placing them in mycombobox.items, so that the super toString() would be called, but leave the objects as a type compatible with myBaseClass

the problem is that when i try to perform the cast it fails in runtime due to downcasting (or upcasting as some would call it)
for each MBC as myBaseClass in myBaseClass.getInstances
  me.mycombobox.items.add(ctype(MBC,mySuperClass))
next

Open in new window


I am looking for ways to get the object in that combobox as some representation of mybaseclass but get the combobox item renderer (internal paint method or whatever) to call a custom toString

some limitations or strategies i wish to avoid
- moving the getInstances function out of myBaseClass, or copy it somewhere else
- having to cast or manipulate the combobox.selectedItem later in the code to get a myBaseClassObject
- do not want to change the combobox control to something custom

I have a feeling that an interface may be part of a good solution but i just cant nail it down.


Thanks for your time and i look forward to hearing your questions, and solutions!
Anthony
Comment
Watch Question

CERTIFIED EXPERT
Top Expert 2015

Commented:
Try DirectCast instead of CType, same syntax.

CType tries to do a conversion, while DirectCast works directly with the pointer. Sometimes one works but not the other.
That is strange. I tried the same thing in C# and there it works like expected,  what should be the case as it uses the polymorphism mechanism to determine which implementation to call.

using System.Collections.Generic;
using System.Windows.Forms;

namespace WindowsFormsApplication1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();

            List<BaseClass> list = new List<BaseClass>();
            list.Add(new BaseClass());
            list.Add(new SubClass());
            list.Add(new SubClass());
            list.Add(new BaseClass());

            foreach (BaseClass item in list) {
                this.comboBox1.Items.Add(item);
            }
        }        
    }

    public class BaseClass {
        public override string ToString() {
            return "BaseClass to string";
        }
    }

    public class SubClass : BaseClass {
        public override string ToString() {
            return "SubClass to string";
        }
    }
}

Open in new window

CERTIFIED EXPERT
Top Expert 2015

Commented:
Your C# code is not the same thing. You have a mix of BaseClass and SubClass elements in the List, while in VB you have only a List of SubClass elements.

It might point to something with the way the ComboBox implements it's default DisplayMember property. The DisplayMember is the property that is called to fill in the control visible list of items.

Here are 2 ideas.

You can also fill a ComboBox by setting it's DataSource property with a collection or an array. If you go me.mycombobox.DataSource = myBaseClass.getInstances, does it work?

Do you really need to use ToString? What if you added a property specifically for display, other than ToString. This property could be declared MustOverride in the base class, so there is no implementation in the base class. The ComboBox whould thus not look there for the method.

In order to use your property as the display value, simply assign its name to the DisplayMember property of the ComboBox: me.mycombobox.DisplayMember = "YourPropertyName".

Author

Commented:
Direct Cast:

 Private Sub DisplayIAs()
        If Me.cmbAllAttrib.InvokeRequired Then
            Me.cmbAllAttrib.Invoke(New Delegates.NullDelegate(AddressOf DisplayIAs))
            Exit Sub
        End If

        '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
        '  Waiting for a better solution to display custom text in the combobox but retain a IA object in the dropdown.
        '   For now i am going to change the default toString of the IA
        '   but that is a temp solution
        '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

        For Each IA As clsItemAttrib In clsItemAttrib.GetAll()
            Me.cmbAllAttrib.Items.Add(DirectCast(IA, DisplayIA))
        Next
        'Adding the attributes this way causes issues when multiple attributes have the same name
        'Me.cmbAllAttrib.Items.AddRange(clsItemAttrib.GetAll().ToArray)

    End Sub

    Private Class DisplayIA
        Inherits clsItemAttrib
        Private Sub New(ByVal IAID As Integer)
            MyBase.New(IAID)
        End Sub
        Public Overrides Function ToString() As String
            Return Me.ItemAttribName & " (" & Me.ItemAttribType & ")"
        End Function
    End Class

Open in new window


throws
InvalidCastException on directcast line


setting the datasource gets the objects in the cmb.items but no matter what a say for the displaymember it calls and displays ToString
        Me.cmbAllAttrib.DisplayMember = "ToString" '"nonsense", "ItemAttribID","AnyOtherMethodName"
        Me.cmbAllAttrib.DataSource = clsItemAttrib.GetAll()
        Try
            If CType(Me.cmbAllAttrib.Items(0), clsItemAttrib).AttribType = clsItemAttrib.AttribTypes.MSD Then
                'do nothing
            End If
            AsyncMsgbox("Cast Succeded")
        End Try

Open in new window



"Do you really need to use ToString?" :
I don't,  the combobox does tho.

"What if you added a property specifically for display, other than ToString."
If i could specify that method be called by the combobox then i would accept that

"This property could be declared MustOverride in the base class so there is no implementation in the base class. "
the base class cannot be mustInherit it is used extensively as is throughout the project
however i am ok with overridable, and just throw an error if called directly.

"The ComboBox whould thus not look there for the method."
but since the datasource is of type List(of baseclass) how would it know WHERE to look for the superclass overriding method?  i.e. there nothing pointing the cmb to a specific superclass.
CERTIFIED EXPERT
Top Expert 2015
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION

Author

Commented:
changing the baseclass (clsItemAttrib)'s cmdDsiplay method to a readonly property worked like a charm for the displaymember.  Cheers!

if i make that property mustoveride i get

'clsItemAttrib' must be declared 'MustInherit' because it contains methods declared 'MustOverride'.

I also tried declaring the clsItemAttrib as a partial class but that did nothing

if you are saying to make the superclass (DisplayIA) cmbDisplay property mustoveride, that seems like the opposite of what i'm trying to do. (DisplayIA should have the custom display code)

as DisplayIA inherits clsItemAttrib, not the other way around.

I feel that just creating a property specifically for display in combobox'es in the base clsItemAttrib (and other similar classes) will solve the issue at hand.

however i am still interested in a cool way to change whats displayed without changing the base class.
CERTIFIED EXPERT
Top Expert 2015

Commented:
Yes, you are right about the MustInherit, I gave you a wrong information. This is what happens when you answer questions at the same time you are doing 2 other things :-). Sorry. If you do not inherit form a class, you cannot override any member, so MustOverride is useless.

Partial does nothing to change the way a class behave. Partial simply means that the class code is separated in many different source files.

I found a few minutes and tried to reproduce your initial problem. I could not. Here is my code for the 2 classes:
Public Class Class1

    Public Overrides Function ToString() As String
        Return "Class 1"
    End Function

End Class

Public Class Class2
    Inherits Class1

    Public Overrides Function ToString() As String
        Return "Class 2"
    End Function

End Class

Open in new window

Here is the code for the ComboBox:
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    Dim list As New Generic.List(Of Class1)
    list.Add(New Class1)
    list.Add(New Class2)
    ComboBox1.DataSource = list

End Sub

Open in new window

It works as expected, displaying "Class 1" and "Class 2".

Something is wrong in your implementation, and looking closer at the code in your original message, I see that it is not real code, otherwise it would not compile.

If New is Private in the base class, you cannot inherit from it.

Publishing pseudo-similar code in a question is not the way to get a straight answer. It leads us to a lot of assumptions that have nothing to do with the code that generated the problem.

Please, check you real code. There is something you have missed somewhere in there.

Author

Commented:
in your code you are creating a brand new class2
list.Add(New Class2)

in my code all i have to work with is a list of class1's
For Each IA As clsItemAttrib In clsItemAttrib.GetAll() 'list(of clsItemAttrib)

what i am trying to do is 'wrap' or cast the clsItemAttrib Objects in or to something else that would allow me to write a custom property on it (for display in a combobox) while still leaving the object as a typeof clsItemAttrib (class1 in your example)

"Publishing pseudo-similar code in a question is not the way to get a straight answer. "
In my attempts to make things more straight forward i may have done the opposite :)


"If New is Private in the base class, you cannot inherit from it."
I was worried about having to tackle that issue as well as it could have implementations later when comparing with  if  Object1 is Object2     and there is a possibility of some caching issues with my code structure.
CERTIFIED EXPERT
Top Expert 2015

Commented:
"in your code you are creating a brand new class2"
This is what you were doing when you showed code that was working in C#:
list.Add(new BaseClass());
list.Add(new SubClass());
list.Add(new SubClass());
list.Add(new BaseClass());

Open in new window


If you instantiate an object on the BaseClass, do not expect to be able to cast it to a derived class, this is not possible. And object can only be cast to its base class or a class that is lower in the hierarchy, not the other way around.

You will never be able to find an Image property on a TextBox, not matter what you try to do, because none of the classes leading to the TextBox have an Image property.

I am not sure I follow what you are trying to do, but if I understand well what you mean by "allow me to write a custom property on it (for display in a combobox) while still leaving the object as a typeof clsItemAttrib (class1 in your example)", no inheritance or cast is needed. Simply add a ComboBoxDisplay property to clsItemAttrib and use it as a DisplayMember. This would be something akind to the Tag property that Microsoft has defined as a generic custom property on Windows controls.

Author

Commented:
That snippet is from codemasteralex 's response.

I fully agree with you about the not being able to cast 'up' the heirarchy

the spirit of this question was to find alternatives to that.

like i said before the displaymember property of the combobox will solve my issue.

and i thought a bit more about what you said about partial classes being in multiple files and i played around with that, and i found i could write that "custom" property to call via the combobox displaymember in the same file as the form bye making the class partial.

this will allow me to create lots of 'custom' properties on the same file they are bing used without having to edit the main baseclass file.

not the cleanest or perhaps most secure way to go about it but it does have a layer of housekeeping to it.

is there any red flags that raise with that approach?
CERTIFIED EXPERT
Top Expert 2015

Commented:
Yes, there are flags. Having different "versions" of the same class will give you headaches.

The best thing to do usually in a situation such as the one you present is either to define a base class as an abstract class (MustInherit), with a MustOverride method, the idea I presented earlier, either through a generic property that can be used anyway you want, such as the Tag on controls. If you intend to use such a property for the sole purpose of displaying it in ComboBoxes, do not make it return an Object as Tag does however. Be more specific, make it a String to be sure that the ComboBox will be able to display it.

But without having the whole picture and the small details, it is not possible to say if it can be applied in your situation.

Author

Commented:
Thank you for all the great feedback.

the project is so large at this point trying to go over all of it for the sake of this post might be an exercise in futility. and render any answers useless to the observers.  but in short the classes such as clsItemAttrib are used so heavily throughout that i feel making those mustinherit would cause allot more work/headache than just making them partial for the sake of separation.

again thanks for the insight!

Author

Commented:
Using the datasource and display member properties of the combobox allows me to call a specific property of the base class for display.

I have chosen to use a partial class so that the display property for a specific combobox can be on the same file as that controls form code.

this removes the need for casting / inheritance etc..

Thank you JamesBurger
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.