繁体   English   中英

从基类获取方法?

[英]Getting methods from base class?

我是基础类的新手,所以如果我错了或者应该以另一种方式进行操作,请更正我,但是我在下面编写了一个简单的代码段,以解释我想要做什么。

class A : B
{
    private int _classId;

    public A (int id) : base(id)
    {
        _classId = id;
    }

    public string GetString()
    {
        return "This will eventually be an important string.";
    }
}

class B
{
    private int _classId;

    public B (int id)
    {
        // here I want to be able to get access to class A's methods
        // such as GetString() ...
    }

    public void SomeMethod()
    {

    }
}

所以我想做的就是让B获得A的方法。

这样做的原因? 我不想填充类A,因为B将有很多方法,因此将它们保留在B中,然后从其他代码中从A中获取它们会更清洁。 我做错了吗? 这不是基类做什么吗? 我只是想仍然能够做到这一点:

A.SomeMethodInB();

无需在A类中填充垃圾并通过将其存储在B类中使其变得更清洁

让我总结一下您的问题:

  • 您需要仅在后代类(A)中实现的方法
  • 您希望此方法可用于基类(B)和子代类(A)
  • 您希望基类能够调用方法的后代类的实现

这是多态性的最初定义(最近人们一直在更广泛地使用该术语),并且在涉及OOP时是头疼的。 对于新来者来说,这也是OOP最难掌握的领域。

用虚方法写基类

让我们从基类开始。 请注意,由于您稍后将要解释的原因,我对您的示例进行了一些更改。

class B
{
    virtual protected string GetString()
    {
        return "I am a B";
    }

    public void DoSomethingWithGetString()
    {
        Console.WriteLine(GetString());
    }
}

在此示例中, DoSomethingWithGetString调用GetString 请注意, GetString是虚拟的。 这告诉编译器,我们不想在运行时决定GetString的实现在哪里。 这意味着它不能在编译时绑定:相反,它的位置由创建对象时建立的运行时虚拟方法表标识。 如果此代码作为类B的实例的一部分运行,则它将调用类B的GetString实现。 但是,如果此代码是继承的,并且实际上是由类B派生的其他某个对象调用的,则它将调用该类的GetString版本。 这称为后期绑定

编写后代类

现在让我们来编写A类:

class A : B
{
    protected override GetString()
    {
        return "I am an A.";
    }
}

因为A来自B,所以我们不需要执行其他任何操作。 如果我们调用A.DoSomethingWithGetString它将实际上调用B的实现,因为它是继承的。

执行它

如果运行此代码

var a = new A();
a.DoSomethingWithGetString();  //Output = "I am an A"

...将发生以下情况:

  1. 创建一个从B派生的A实例
  2. 配置A的VMT,以便将对GetString调用发送到A.GetString。
  3. 您调用A.DoSomethingWithGetString
  4. 该调用直接针对B的代码库,因为A没有实现它。
  5. B中的代码库检测到GetString是虚拟方法,并使用VMT对其进行调用。
  6. VMT指向A的GetString实现。
  7. A.GetString被执行。

因此,您遇到一种情况,即B类中的代码正在调用A类中的代码,这正是您所要求的。

您也可以这样运行它:

B b = new A();
b.DoSomethingWithGetString(); //Output = "I am an A"

因此,您有一个指向B的指针,说它是A,因为它使用的是为类A设置的VMT。

但是我不希望B类有任何实现

现在,假设您不希望B类对GetString完全具有任何实现,但是您仍然希望能够在A中调用该实现。有一种方法可以使用抽象的虚方法来实现 只需添加abstract关键字并删除实现:

abstract class B
{
    abstract protected string GetString(); //Notice no implementation

    public void DoSomethingWithGetString()
    {
        Console.WriteLine(GetString());
    }
}

当然,现在您不能单独使用类B,因为它需要仅在后代类中存在的GetString实现。 因此,您不再可以直接创建B的实例。 您必须先进行子类型化,然后再使用子类型。

不能使用构造函数

现在,您会在示例中注意到我删除了构造函数调用。 有一个原因。 构造函数永远不要调用在后代类上定义的方法。 原因:在构造过程中,构造函数在继承层次结构中上下移动,从基类开始,在子孙链中向下移动。 这意味着,当您调用B的构造函数时,尚未调用A的构造函数。 如果A的构造函数尚未运行,则无法确定A的设置是否正确,因此调用其任何方法是一个非常糟糕的主意。

有一些解决方法,但这是另一个问题的主题。

如果该方法仅在Class A定义,则无法通过Class B访问它。

如果该方法同时存在于Class AClass B ,则可以使用virtual方法并override

认为在示例中最好使用名称BaseClass和DerivedClass。 名称A和B是任意的。 尽管A早于B,但名称并不表示继承的方向。

class Program
    {
        static void Main(string[] args)
        {
            var bc = new BaseClass(3);
            var dc = new DerivedClass(5);

            Console.WriteLine("Base Class Method A: " + bc.MethodA());
            Console.WriteLine("Derived Class Method A: " + dc.MethodA());
            Console.WriteLine("Base Class Method B: " + bc.MethodB());
            Console.WriteLine("Derived Class Method B: " + dc.MethodB());
            Console.ReadLine();
        }
    }

    public class BaseClass {
        protected int _classId;

        public BaseClass(int classId) {
            _classId = classId;
        }

        public virtual string MethodA() {
            return "Method A in base class: " + _classId.ToString();
        }

        public string MethodB()
        {
            return "I am a method B in the base class: " + _classId.ToString();
        }
    }

    public class DerivedClass : BaseClass {
        public DerivedClass(int classId)
            : base(classId)
        {

        }

        public override string MethodA() {
            return "Method A in derived class: " + _classId.ToString();
        }
    }

有两种方法可以解决此问题。 首先让我举一个例子。

这里我们有一个包含方法的基类:

public class Base
{
    public void BaseMethod()
    {
        // Some method.
    }
}

这是派生类,我们要在其中调用该方法:

public class DerivedClass : BaseClass
{
    // We want to use BaseMethod() here.
}

为了从基类中调用方法,我们使用base关键字。

public class DerivedClass : BaseClass
{
    base.BaseMethod();
}

如果您只想调用基本方法,那很好。 但是,如果要更改方法并使用其他函数定义它,则必须在基类中将其声明为virtual ,如下所示:

public class Base
{
    public virtual void BaseMethod()
    {
        // Some method.
    }
}

在派生类中,我们现在可以使用关键字override覆盖基类的虚拟方法,从而将其重新定义为执行其他操作:

public class DerivedClass : BaseClass
{
    public override void BaseMethod()
    {
        // Some new function.
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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