【问题标题】:C# inheritance questionsC#继承问题
【发布时间】:2011-09-01 12:46:06
【问题描述】:

我正在创建一个抽象基类,它具有由其他类实现的功能。我的疑惑如下

1) 我是否需要在每个需要被子类覆盖的函数前面给出“虚拟”?我看到一些没有 virtual 关键字的示例,它们仍然可以被覆盖。

2) 我需要有一个将在基类中实现的函数,并且我不希望它被子类覆盖。我在该函数前面添加了“固定”关键字。编译器开始抱怨“成员”不能被密封,因为它不是覆盖。我在这里做错了吗?

abstract public class ShapeBase
    {
        private ShapeDetails _shapedDetails;

        public CampusCardBase(ShapeDetails shDetails)
        {
            _shapedDetails= shDetails;
        }

        public virtual void Draw();
        public virtual float getWidth();
        public virtual void Swap();
        public virtual void Erase();

        public sealed ShapeDetails getShapeDetails()
        {
            return _shapedDetails;
        }


    };

【问题讨论】:

  • 你错过了那些方法上的'abstract'关键字......如果你不想重写但实现然后使用abstract。

标签: c# .net oop inheritance


【解决方案1】:
  1. 对于在抽象类中没有实现的方法,也可以使用abstract

    abstract public void Draw();
    abstract public float getWidth();
    abstract public void Swap();
    abstract public void Erase();
    
  2. 默认情况下方法是不可覆盖的;它们只允许在声明 abstractvirtualoverride(但不允许 override sealed)时覆盖派生类。

    因此,除了public,您不需要给getShapeDetails() 提供任何其他修饰符:

    public ShapeDetails getShapeDetails()
    {
        return _shapedDetails;
    }
    

附带说明,您应该坚持 .NET 命名约定并使用 Pascal 大小写大写方法名称,因此 getWidth() 变为 GetWidth()getShapeDetails() 变为 GetShapeDetails()

事实上,您应该为您的_shapedDetails 字段使用property getter 而不是getShapeDetails() 方法:

private ShapeDetails _shapedDetails;

public ShapeDetails ShapedDetails
{
    get { return _shapedDetails; }
}

【讨论】:

  • “sealed 关键字仅适用于类声明,而不适用于方法声明”-它可以适用于方法:override sealed 覆盖虚拟/抽象方法并防止下游覆盖。
  • -1:如果成员覆盖了虚拟或抽象成员,则可以密封成员。
  • @BoltClock - 实际上,sealed 关键字可用于两个类和成员,以防止进一步派生。在 OP 的问题中,这不是所需的行为,但在某些边缘情况下,它实际上非常有用,可以封闭不应在更多派生类中覆盖的虚拟方法。不过,对于其余的答案 +1,我相信它可以解决原始问题。
  • @Ani @Phoog - 打败我,需要有一个“在你打字时发布了新评论”警报:)
  • 糟糕,感谢您指出这一点,伙计们。我编辑了我的答案。 @Tim:我一直想说我自己:)
【解决方案2】:

要被覆盖,必须将成员标记为虚拟或抽象。如果是抽象的,则该类也必须是抽象的,并且该成员在定义类中没有实现。虚拟成员必须提供实现。派生类必须覆盖抽象成员并且可以覆盖虚拟成员。非虚拟成员可能不会被“密封”,因为它们无论如何都不能被覆盖。

【讨论】:

    【解决方案3】:

    第 1 点:您可以将这些函数抽象为虚拟函数而不是虚拟函数。

    public abstract void Draw();
    public abstract float getWidth();
    public abstract void Swap();
    public abstract void Erase();
    

    这意味着这些函数必须被覆盖。目前,您可以创建抽象类的子类,其中没有任何定义,它仍然可以编译。通过说这些是抽象函数,必须重写它们。如果你不重写,那么子类也将是抽象的。

    关于第2点:你能添加使用固定词的代码吗?

    【讨论】:

      【解决方案4】:

      1- 参考override (C# Reference)

      您不能覆盖非虚拟或 静态方法。被覆盖的基础 方法必须是虚拟的、抽象的或 覆盖。


      2- 参考sealed (C# Reference)

      你也可以使用密封修饰符 在覆盖的方法或属性上 基础中的虚拟方法或属性 班级。这使您可以允许 从你的类派生的类和 防止他们覆盖特定的 虚拟方法或属性。

      【讨论】:

        【解决方案5】:

        只能重写虚方法,只能实现抽象方法(如接口方法)。否则,唯一能做的就是声明一个新方法来“隐藏”旧方法,如下所示:

        new public void getShapeDetails() {
            // Hides base.getShapeDetails();
        }
        

        这是一个代码示例/说明。

        //-----------------------------------------------------------------------------
        // <copyright file="Program.cs" company="DCOM Productions">
        //     Copyright (c) DCOM Productions.  All rights reserved.
        // </copyright>
        //-----------------------------------------------------------------------------
        
        namespace AbstractClassExample {
            using System;
            using System.Collections.Generic;
            using System.Linq;
            using System.Text;
        
            class Program {
                /// <summary>
                /// Example abstract base class
                /// </summary>
                public abstract class FooClass {
        
                    /// <summary>
                    /// Abstract Method
                    /// </summary>
                    public abstract void abstractFoo();
        
                    /// <summary>
                    /// Virtual Method
                    /// </summary>
                    public virtual void virtualFoo() {
                        // Todo
                    }
        
                    /// <summary>
                    /// Normal Method
                    /// </summary>
                    public void Foo() {
                        // Todo
                    }
        
                }
        
                public class FooDeriver : FooClass {
                    /// <summary>
                    /// Implements base.abstractFoo
                    /// </summary>
                    public override void abstractFoo() {
                        throw new NotImplementedException();
                    }
        
                    /// <summary>
                    /// Overrides base.virtualFoo
                    /// </summary>
                    public override void virtualFoo() {
                        base.virtualFoo();
                    }
        
                    /// <summary>
                    /// Compiler ErrorError 1
                    /// cannot override inherited member 'ConsoleApplication1.Program.FooClass.Foo()' because it is not marked virtual, abstract, or override
                    /// </summary>
                    public override void Foo() {
                        throw new NotImplementedException();
                    }
                }
        
                static void Main(string[] args) {
                    // Program code
                }
            }
        }
        

        【讨论】:

          【解决方案6】:

          如果是抽象方法,该方法默认为virtual,需要在继承类中实现。 否则,如果它不是抽象的,你需要有 virtual 关键字。如果您不这样做,将使用编译时类型方法。

          【讨论】:

          • -1:抽象类的成员默认不是抽象的;它们必须用“abstract”关键字标记为抽象。
          • 现在一切都清楚了。不幸的是,即使您似乎在我投票后编辑了答案,SO 也不会让我删除我的反对票:(
          • @phoog:他可能设法在 5 分钟的窗口内潜入编辑。不管怎样,我踢了它一下,你现在应该可以换票了。
          • 是的,谢谢。而且,为了记录,“投票”,而不是“投票”!
          【解决方案7】:
          1. 如果你想在你的基类中提供一个可以在派生类中被覆盖的默认实现,你可以用virtual键标记函数;或者您可以将其标记为abstract,将实现委托给派生类。
          2. 如果您不希望某个函数在子实现中被覆盖,请勿将其标记为virtualabstract:只需像平常一样定义该函数即可。

          【讨论】:

            猜你喜欢
            • 2012-05-11
            • 1970-01-01
            • 1970-01-01
            • 2021-01-04
            • 2010-11-25
            • 2012-05-11
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多