0

The question of downcasting has been raised on SO before, but I couldn't find any explanation on how to avoid it properly.

Consider this hierarchy, for example.

abstract class NetworkDevice
{
    //some common fields & properties
}

class Router : NetworkDevice
{
    //some router-specific fields & properties & methods
}

class Switch : NetworkDevice
{
    //some switch-specific fields & properties & methods
}

At some points I want to treat routers and switches and the rest of networking devices similary, based on their common properties (hence the base class), but at some points - differently, considering their unique qualities.

The question is - how do I properly switch from one case to another?

Say, there's List<NetworkDevice>, received from some source. Now, when user selects one NetworkDevice from the list, I need to show all the specific information (switch/router/etc specific) and populate visual controls with values.

The way I understand, someone has to downcast NetworkDevice to a derived class to do that, but how would that someone go about it so that the incapsulation is preserved?

The only way to go about it that I can see at the moment is to modify NetworkDevice like this:

abstract class NetworkDevice
{
    Type type {get; set;} 
    //etc...
}

or to define Enum NetworkDeviceType { Router, Switch, ...} for the same purpose.

Either way we end up with this kind of code:

void DifferenciateNetworkDevice (NetworkDevice device)
{
    ...
    switch (device.type)
    {
        case Switch: //or NetworkDeviceType.Switch
        ShowSwitchProperties(device as Switch);
        break;

        case Router: //or NetworkDeviceType.Router
        ShowRouterProperties(device as Router);            
        break;
        ...
    }
}

That leads to long switch statements and doesn't seem very encapsulating. So, what am I missing? What would be the right way to go about it?

P.S. Reflection, I hope, wouldn't be a common approach in this situation and could be avoided. P.P.S. English is my second language, so I'm sorry for the mistakes.

Korli
  • 478
  • 4
  • 9
  • What's wrong with overriding `ToString` on `NetworkDevice`, then supplement that with `Router.ToString`? e.g. `NetworkDevice.ToString() { return "basic network device info"; }` `Router.ToString() { return base.ToString() + "additional router info"; }` – Brad Christie Mar 02 '16 at 16:17
  • It's not really possible because you can't know all the types of derived class, anyone can add one in the future and break your code. Why not just have an abstract method of show properties and have each derived class implement it differently ? – user1450877 Mar 02 '16 at 16:18
  • Oh, i see now; you're looking to target specific controls on a form based on the type. You may be able to use something more dynamic (such as a dictionary) and target controls to specific key values. e.g. `IpAddress = networkDevice.Ip` and `RouteTable = networkDevice.Properties["routingTable"]` Otherwise, consider contracts (such as `IRouter` and `ISwitch` and check for that when you go to populate controls), or, I suppose, just check for specific types (`if (networkDevice is Router) { /* populate router controls */ }`). – Brad Christie Mar 02 '16 at 16:20
  • You may be able to use [co/contravariance](https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx) to your advantage. – Crowcoder Mar 02 '16 at 16:21
  • *"when user selects one `NetworkDevice` from the list, I need to show all the specific information"* - that depends on technology. E.g. in WPF you can use data templates *magic* to visualize collection of objects differently depending on the object type. – Sinatr Mar 02 '16 at 16:21
  • @Brad-Christie, I need something more easily manageable than a string to parse. Consider databindings and capability to set those specific properties on user input. – Korli Mar 02 '16 at 16:24
  • @user1450877, is there an easy, common way to go about it? Making device-classes aware of presentation layer seems dangerous too. Keeping them ignorant of any presentation-related logic seems better design-wise. – Korli Mar 02 '16 at 16:31
  • @Korli Perhaps invent a new class whose only purpose is to hide away the grungy type checking in a single method, and use that to orchestrate the UI populating. – Matthew Watson Mar 02 '16 at 16:36
  • @BradChristie, that's right, thanks for understanding. I need to correlate specific controls and property values. Checking for types is unavoidable, it seems. – Korli Mar 03 '16 at 08:19

3 Answers3

0

You actually don't. Consider that you have the method .Flush() in all 3 layers of abstraction, you can "override" non-virtual methods using the keyword "new" as such:

abstract class NetworkDevice
{
    public void Flush() {
        // Logic
    }

    public void PowerDown() {
        // Logic
    }
}

public class Router : NetworkDevice
{
    // New stuff, ignoring the one below
    public new void Flush() { 
        // new magic
    }

    public void Routaree() {
        // router specific magic
    }
}

public class Switch : NetworkDevice
{
    public void Switcharoo() {
        // Switch centric stuff
    }        
}

In the example above, both Router and Switch will use a new implementation for "Flush()", different from the abstract class implementation.

You could also put "virtual" in front of methods that need re-implementation and use "override" instead of "new" to achieve pretty much the same.

Pedro G. Dias
  • 3,162
  • 1
  • 18
  • 30
  • I agree, what I am consirned about is the behaviour external (as it seems to me) to all these classes. Should `Switches`/`Routers`/etc really have specific methods to control visual representation (winforms related logic in my case). – Korli Mar 03 '16 at 08:22
  • No, you shouldn't have any methods related to visual representation. https://en.wikipedia.org/wiki/Single_responsibility_principle this dictates that a class should only have one, single reason to be changed. – Pedro G. Dias Mar 03 '16 at 08:40
0

I would just use ShowProperties(device.GetType().ToString());

This ShowProperties method would populate visual controls with values depending on the string parameter it receives.

Is that what you wanted?

Mobaruk H
  • 54
  • 1
  • 7
0

Every time you have a common behavior among your classes you should represent that as a method inside the base class.

Whenever a derived class or subset of classes needs to change the behavior from the base class you should override the method. This concept is know as polymorphism.

I would avoid using the "new" keyword as suggested by Pedro, but this is just my opinion.

EduardoCMB
  • 392
  • 2
  • 17
  • The way I see this, it is more about the behaviour external to these classes than their own behaviour. Am I wrong when thinking, that neither of them is to know anything about the presentation logic (i.e. which controls to databind values to and how)? But the presenter or the view should be able to differenciate between different Devices to show relevant controls somehow, should they not? – Korli Mar 03 '16 at 09:42