Odd Shadowing behaviour

K

Karsten Schramm

Hi,

I tried to show someone the general idea of shadowing.

Then in my last step I stumbled.

This is the original code that worked exactly as expected:


-----
using System;
using System.Collections.Generic;

namespace VirtualOverride
{
class Program
{
static void Main()
{
Alarm alarm = new Alarm(5);
Console.WriteLine(alarm.GetDisplayClass());
Console.WriteLine(alarm.GetSomething());
TypeAlarm typeAlarm = new TypeAlarm(12);
typeAlarm.Name = "Test";
Console.WriteLine(typeAlarm.GetDisplayClass());
Console.WriteLine(typeAlarm.GetSomething());
InstanceAlarm instanceAlarm = new InstanceAlarm(17, typeAlarm);
Console.WriteLine(instanceAlarm.GetDisplayClass());
Console.WriteLine(instanceAlarm.GetSomething());
InstanceAlarm instanceAlarm2 = new InstanceAlarm(typeAlarm);
Console.WriteLine(instanceAlarm2.GetDisplayClass());
Console.WriteLine(instanceAlarm2.GetSomething());

Console.WriteLine();
Console.WriteLine();

List<Alarm> alarms = new List<Alarm>(4);
alarms.Add(alarm);
alarms.Add(typeAlarm);
alarms.Add(instanceAlarm);
alarms.Add(instanceAlarm2);
foreach (Alarm alarmIterator in alarms)
{
Console.WriteLine(alarmIterator.GetDisplayClass());
Console.WriteLine(alarmIterator.GetSomething());
}

Console.WriteLine();
Console.WriteLine();

Alarm instanceAlarm3a = new InstanceAlarm(47, typeAlarm);
Console.WriteLine(instanceAlarm3a.GetDisplayClass());
Console.WriteLine(instanceAlarm3a.GetSomething());
InstanceAlarm instanceAlarm3b = (InstanceAlarm)instanceAlarm3a;
Console.WriteLine(instanceAlarm3b.GetDisplayClass());
Console.WriteLine(instanceAlarm3b.GetSomething());

Console.ReadLine();
}
}

interface IAlarm
{
Int32 GetDisplayClass();

String GetSomething();
}

interface ITypeAlarm : IAlarm
{
}

class Alarm : IAlarm
{
private Int32 m_DisplayClass;

public Alarm(Int32 displayClass)
{
this.m_DisplayClass = displayClass;
}

public virtual Int32 GetDisplayClass()
{
return (this.m_DisplayClass);
}

public String GetSomething()
{
return ("This is an Alarm");
}
}

class TypeAlarm : Alarm, ITypeAlarm
{
public String Name;

public TypeAlarm(Int32 displayClass)
: base(displayClass)
{
}

public override Int32 GetDisplayClass()
{
return (base.GetDisplayClass());
}

public new String GetSomething()
{
return ("This is a TypeAlarm");
}
}

class InstanceAlarm : Alarm
{
private TypeAlarm m_TypeAlarm;

public InstanceAlarm(Int32 displayClass, TypeAlarm typeAlarm)
: base(displayClass)
{
this.m_TypeAlarm = typeAlarm;
}

public InstanceAlarm(TypeAlarm typeAlarm)
: this(-1, typeAlarm)
{
}

public override Int32 GetDisplayClass()
{
Int32 displayClass;

displayClass = base.GetDisplayClass();
if (displayClass == -1)
{
return (this.m_TypeAlarm.GetDisplayClass());
}
else
{
return (displayClass);
}
}

public override String ToString()
{
return (String.Format("InstanceAlarm of TypeAlarm '{0}'", this.m_TypeAlarm.Name));
}

public new String GetSomething()
{
return ("This is an InstanceAlarm");
}
}
}
-----


No I moved the GetSomething() method to the interface IAlarm and built a second iterator:


-----
using System;
using System.Collections.Generic;

namespace VirtualOverride
{
class Program
{
static void Main()
{
Alarm alarm = new Alarm(5);
Console.WriteLine(alarm.GetDisplayClass());
Console.WriteLine(alarm.GetSomething());
TypeAlarm typeAlarm = new TypeAlarm(12);
typeAlarm.Name = "Test";
Console.WriteLine(typeAlarm.GetDisplayClass());
Console.WriteLine(typeAlarm.GetSomething());
InstanceAlarm instanceAlarm = new InstanceAlarm(17, typeAlarm);
Console.WriteLine(instanceAlarm.GetDisplayClass());
Console.WriteLine(instanceAlarm.GetSomething());
InstanceAlarm instanceAlarm2 = new InstanceAlarm(typeAlarm);
Console.WriteLine(instanceAlarm2.GetDisplayClass());
Console.WriteLine(instanceAlarm2.GetSomething());

Console.WriteLine();
Console.WriteLine();

List<Alarm> alarms = new List<Alarm>(4);
alarms.Add(alarm);
alarms.Add(typeAlarm);
alarms.Add(instanceAlarm);
alarms.Add(instanceAlarm2);
foreach (Alarm alarmIterator in alarms)
{
Console.WriteLine(alarmIterator.GetDisplayClass());
Console.WriteLine(alarmIterator.GetSomething());
}

Console.WriteLine();
Console.WriteLine();

Alarm instanceAlarm3a = new InstanceAlarm(47, typeAlarm);
Console.WriteLine(instanceAlarm3a.GetDisplayClass());
Console.WriteLine(instanceAlarm3a.GetSomething());
InstanceAlarm instanceAlarm3b = (InstanceAlarm)instanceAlarm3a;
Console.WriteLine(instanceAlarm3b.GetDisplayClass());
Console.WriteLine(instanceAlarm3b.GetSomething());

Console.WriteLine();
Console.WriteLine();

List<IAlarm> ialarms = new List<IAlarm>(4);
ialarms.Add(alarm);
ialarms.Add(typeAlarm);
ialarms.Add(instanceAlarm);
ialarms.Add(instanceAlarm2);
foreach (IAlarm alarmIterator in ialarms)
{
Console.WriteLine(alarmIterator.GetDisplayClass());
Console.WriteLine(alarmIterator.GetSomething());
}

Console.ReadLine();
}
}

interface IAlarm
{
Int32 GetDisplayClass();

String GetSomething();
}

interface ITypeAlarm : IAlarm
{
}

class Alarm : IAlarm
{
private Int32 m_DisplayClass;

public Alarm(Int32 displayClass)
{
this.m_DisplayClass = displayClass;
}

public virtual Int32 GetDisplayClass()
{
return (this.m_DisplayClass);
}

public String GetSomething()
{
return ("This is an Alarm");
}
}

class TypeAlarm : Alarm, ITypeAlarm
{
public String Name;

public TypeAlarm(Int32 displayClass)
: base(displayClass)
{
}

public override Int32 GetDisplayClass()
{
return (base.GetDisplayClass());
}

public new String GetSomething()
{
return ("This is a TypeAlarm");
}
}

class InstanceAlarm : Alarm
{
private TypeAlarm m_TypeAlarm;

public InstanceAlarm(Int32 displayClass, TypeAlarm typeAlarm)
: base(displayClass)
{
this.m_TypeAlarm = typeAlarm;
}

public InstanceAlarm(TypeAlarm typeAlarm)
: this(-1, typeAlarm)
{
}

public override Int32 GetDisplayClass()
{
Int32 displayClass;

displayClass = base.GetDisplayClass();
if (displayClass == -1)
{
return (this.m_TypeAlarm.GetDisplayClass());
}
else
{
return (displayClass);
}
}

public override String ToString()
{
return (String.Format("InstanceAlarm of TypeAlarm '{0}'", this.m_TypeAlarm.Name));
}

public new String GetSomething()
{
return ("This is an InstanceAlarm");
}
}
}
-----

The result of that last iteration returns:


-----
5
This is an Alarm
12
This is a TypeAlarm
17
This is an Alarm
12
This is an Alarm
 
K

Karsten Schramm

Am Samstag, 20. Oktober 2012 02:14:00 UTC+2 schrieb Peter Duniho:
More generally, when a class is declared as implementing an interface, the

interface members found in that class take precedence over base class

members (if a member is not found, then the members are used from the

most-derived base class).



In your example, InstanceAlarm didn't implement IAlarm...its base class

did. So that's where the implemented method comes from. But since

TypeAlarm is declared as implementing IAlarm, and it in fact does have an

implementation of the GetSomething() member of that interface, then its

implementation takes precedence.



Of course, it's worth pointing out that member hiding is almost always a

bad idea. It creates code that is difficult to understand and often behaves

in unexpected ways, in part because of the very issue you're asking about.

In general, derived types should use the same member name as an existing

member only when the base class's member is virtual and so can be

overridden. And in that case, an override is called for, not hiding.



Hope that helps. Please see below for a much simpler code example that

demonstrates the same thing.



Pete





using System;



namespace TestInterfaceHiding

{

class Program

{

interface IInterface

{

void Method();

}



class Base : IInterface

{

public void Method() { Console.WriteLine("Base"); }

}



class Derived1 : Base

{

public new void Method() { Console.WriteLine("Derived1"); }

}



class Derived2 : Base, IInterface

{

public new void Method() { Console.WriteLine("Derived2"); }

}



static void Main(string[] args)

{

((IInterface)new Base()).Method();

((IInterface)new Derived1()).Method();

((IInterface)new Derived2()).Method();

}

}

}

Thanks,

after a while I figured that this (interface implementation) must be the key difference.

And yes, ten years ago I approached .Net via VB.Net and the term Shadowing has been in my head ever since.

I don't use shad---- hiding unless it's absoluetely necessary (99.5% of the time it's not) but I tried to explain a newbie the difference between overriding and sh-- hiding and stumbled upon this thing.

Thanks for your explanation. :)
 

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