简体   繁体   中英

Call derived class method when using base class in foreach loop

I have the following code which simply loops through a list of EdiElements and validates each one to make sure that it conforms to a set of rules defined as attributes on the derived class.

The problem I am having is with the elem.Validate() line which is calling the base class method instead of the derived class method. The _elements list contains different classes that all derive from EdiElement and I need to be able to loop through them all and perform validation rules in each of these classes.

What is the standard way of calling a method on a derived class when using the base class as an iterator variable in a foreach loop as in the example below?

public override bool Validate()
{
  foreach ( EdiElement elem in _elements )
  {
    if ( !_checkElementIsValid( this, elem ) )
      base.ValidationErrors.Add( string.Format( "Element {0} does not belong to segment {1}", this.GetType().Name, this.GetType().Name ) );
    else
      elem.Validate(); // <-- I want this to call derived class method
  }

  return base.ValidationErrors.Count == 0;
}

Edited to show class definitions.

public abstract class EdiElement : Edi
{
  public override bool Validate()
  {
    return true; // breakpoint here gets hit
  }
}

public abstract class EdiDataElement<T> : EdiElement
{
  public T Value { get; set; }
}

public class E3286 : EdiDataElement<String>
{
  public override bool Validate()
  {
    return base.Validate(); // breakpoint here does not get hit!
  }
}

I forgot to add the base class.

public abstract class Edi
{
    protected static List<string> _validationErrors = new List<string>();

    public List<string> ValidationErrors
    {
        get
        {
            return _validationErrors;
        }
    }

    public abstract bool Validate();
}

I have managed to resolve it after changing the EdiElement so that it defines an abstract override in the Validate() method and then override this in the derived classes. The code is now executing correctly in the manner that I expected.

public override bool Validate()
{
  foreach ( EdiElement elem in _elements )
  {
    if ( !_checkElementIsValid( this, elem ) )
      base.ValidationErrors.Add( string.Format( "Element {0} does not belong to segment {1}", this.GetType().Name, this.GetType().Name ) );
    else
      elem.Validate(); // <-- I want this to call derived class method
  }

  return base.ValidationErrors.Count == 0;
}

public abstract class EdiElement : Edi
{
  public abstract override bool Validate();
}

public abstract class EdiDataElement<T> : EdiElement
{
  public T Value { get; set; }
}

/// <summary>
///  To specify a component of an address.
/// </summary>
[DataElementMetadata(
    Name = "Address component",
    Description = "To specify a component of an address.",
    Type = Types.Type.an,
    Length = "0..70" )]
public class E3286 : EdiDataElement<String>
{
    public override bool Validate()
    {
        base.ValidationErrors.Add( "Validation error!" );
        return false;
    }
}

I'd like to thank the community for pointing me in the right direction as I was sure I was doing something wrong.

I think what is going on is somehow (despite debug build and settings) the compiler is optimizing out the call to base.Validate() which is why the breakpoint is not being hit.

The following (as per the original class design) works correctly for me:

A x = new D();
x.Test();

public abstract class A
{
    public abstract void Test();
}

public abstract class B : A
{
    public override void Test()
    {
        Console.WriteLine("Test() in B");
    }
}

public abstract class C<T> : B
{

}

public class D : C<String>
{
    public override void Test()
    {
        base.Test(); //breakpoint hits here
    }
}

That is a...peculiarity of C#.

If one defines a base class and inherits from it, the default behaviour is that, when an instance is upcast to the base class, overridden methods revert to the base class implementation of the overridden method, unless the override is explicitly marked with the keyword override .

If you ask me, this violates a basic tenet of OO programming (a dog doesn't cease being a dog if you call it an animal). Personally, I think C#'s designers were crocked when the decided on this behavior, but I digress.

For instance this program:

class Animal         { public virtual  void Speak() { Console.WriteLine("Most animals make some sort of noise.") ; } }
class Dog : Animal   { public virtual  void Speak() { Console.WriteLine("Dog: Woof!") ; } }
class Cat : Animal   { public new      void Speak() { Console.WriteLine("Meow!") ; } }
class Sheep : Animal { public override void Speak() { Console.WriteLine("Baaa!") ; } }

static void Main( string[] args )
{
  Animal[] menagerie = { new Animal() , new Dog() , new Cat() , new Sheep() , } ;

  foreach ( Animal creature in menagerie )
  {
    Console.Write( "{0}:\t" , creature.GetType().Name ) ;
    creature.Speak() ;
  }

  return;
}

produces this output:

Animal: Most animals make some sort of noise.
Dog:    Most animals make some sort of noise.
Cat:    Most animals make some sort of noise.
Sheep:  Baaa!

This feature means that in a C# program, to get the behavior one would ordinarily expect requires extra work on the part of the developer. Unless there is a specific requirement otherwise, it's good practice to always use abstract / virtual and override .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM