【问题标题】:Call derived class method when using base class in foreach loop在 foreach 循环中使用基类时调用派生类方法
【发布时间】:2014-06-25 18:38:03
【问题描述】:

我有以下代码,它简单地遍历一个 EdiElements 列表并验证每个元素以确保它符合定义为派生类属性的一组规则。

我遇到的问题是elem.Validate() 行调用基类方法而不是派生类方法。 _elements 列表包含所有从 EdiElement 派生的不同类,我需要能够遍历它们并在每个类中执行验证规则。

如下例所示,在 foreach 循环中使用基类作为迭代器变量时,调用派生类方法的标准方法是什么?

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 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!
  }
}

我忘记添加基类了。

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

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

    public abstract bool Validate();
}

【问题讨论】:

  • 类/方法是如何定义的?即派生类如何继承/覆盖基类的方法?
  • virtual 函数的默认行为是使用派生方法。你能告诉我们类定义吗?
  • 同意 Ben N。参见msdn.microsoft.com/en-us/library/ms173152.aspx,它说 “在运行时,当客户端代码调用该方法时,CLR 查找对象的运行时类型,并调用覆盖虚方法。因此,在您的源代码中,您可以调用基类上的方法,并导致执行派生类的方法版本。"
  • 您使用的是virtual,对吗?如果您使用new“隐藏”,那将导致您试图避免的那种行为。

标签: c#


【解决方案1】:

我在更改EdiElement 后设法解决了它,以便它在Validate() 方法中定义一个abstract override,然后在派生类中覆盖它。代码现在以我预期的方式正确执行。

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;
    }
}

我要感谢社区为我指明了正确的方向,因为我确信自己做错了。

【讨论】:

  • 我很好奇,如果您在 E3286 Validate() 方法中只有 base.Validate(),它现在是否会遇到断点?
  • 是的,它现在确实在派生类中到达断点,在 E3286 Validate() 方法内。
  • 即使里面只有base.Validate()?
  • 各位,能不能给我解释一下:abstract override是什么?有链接吗?
  • @Love - 这意味着它既是override(替换父母的版本)又是abstract(声明没有自己的定义)。总之,它们意味着您将不得不在子类中再次覆盖它。
【解决方案2】:

我认为发生了什么(尽管有调试构建和设置)编译器正在优化对 base.Validate() 的调用,这就是没有命中断点的原因。

以下(根据原始课程设计)对我来说是正确的:

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
    }
}

【讨论】:

  • 优化代码选项在构建属性中未勾选,我正在使用调试构建。
  • @MikeClarke 如果仅通过更改其中的代码来执行,它一定会以某种方式得到优化。
  • 我将审查我的代码并对其进行修改,使其或多或少与您的实现相匹配。
【解决方案3】:

这是... C# 的一个特点。

如果定义了一个基类并从它继承,则默认行为是,当一个实例向上转换为基类时,被覆盖的方法将恢复为被覆盖方法的基类实现,除非该覆盖被显式标记为关键字override

如果你问我,这违反了 O-O 编程的基本原则(如果你称它为动物,狗并不会停止成为狗)。就我个人而言,我认为 C# 的设计者在决定这种行为时被坑了,但我离题了。

例如这个程序:

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;
}

产生这个输出:

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!

此功能意味着在 C# 程序中,要获得通常预期的行为,开发人员需要额外的工作。除非另有特殊要求,否则始终使用abstract/virtualoverride 是一种很好的做法。

【讨论】:

  • 您会收到有关使用virtual 方法覆盖virtual 方法的编译器警告。不是错误,但会警告您它不会按照您的预期进行。
  • 问题中的类确实使用了override关键字。
  • @mclaassen 是对的。只有 Sheep 示例对问题有效,并且行为正确。
  • 这个答案提出了非常主观的意见,就好像它们是毫无疑问的客观真理一样。例如,我个人“通常会期望” overriding 在指定时发生 only(在这种情况下,通过使用 override 关键字),但这个答案声称确切相反,好像这是唯一正确的方式。
猜你喜欢
  • 1970-01-01
  • 2014-09-05
  • 2019-07-25
  • 1970-01-01
  • 2014-03-17
  • 2016-01-01
  • 2018-10-12
  • 2019-04-25
  • 1970-01-01
相关资源
最近更新 更多