【问题标题】:Getting methods from base class?从基类获取方法?
【发布时间】:2017-02-24 21:14:22
【问题描述】:

我对基类是全新的,所以如果我错了或者我应该以另一种方式这样做,请纠正我,但我在下面编写了一个简单的代码 sn-p 来解释我想要做什么。

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 类中使其更清洁

【问题讨论】:

  • 您应该查看此参考以了解受保护的关键字:msdn.microsoft.com/en-us/library/bcd5672a.aspx
  • 我认为,您的基类和派生类放错了位置,根据您的解释,我理解,您应该将 A 类与许多受保护的方法作为基类,并让 b 类从中派生
  • 所以我想做的是让 B 获得 A 的方法。不——情况正好相反。 AB 获取方法。如果你想让B中的方法调用A中的方法,在B中声明方法virtual,在`A'中声明override

标签: c# .net base


【解决方案1】:

让我总结一下你的问题:

  • 您需要一个仅在后代类 (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 的实现在哪里。这意味着它不能在编译时绑定:相反,它的位置由创建对象时设置的运行时Virtual Method Table 标识。如果此代码作为 B 类实例的一部分运行,那么它将调用 B 类的 GetString 实现。 但是如果这段代码是被继承的,并且实际上被从类 B 派生的某个其他对象调用,它将调用该类的 GetString 版本。 这称为 late binding。 p>

编写后代类

所以现在让我们编写 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 virtual method 来实现。只需添加 abstract 关键字并删除实现:

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

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

当然,现在你不能单独使用 B 类,因为它需要一个 GetString 的实现,它只会出现在后代类中。所以不能再直接创建 B 的实例;你必须子类型,并使用子类型。

不能使用构造函数

现在,您会注意到在我的示例中我删除了您的构造函数调用。有一个原因。 构造函数不应该调用定义在后代类上的方法。原因:在构造过程中,构造函数在继承层次结构中上下调用,从基类开始,向下移动到后代链.也就是说,当你调用 B 的构造函数时,A 的构造函数还没有被调用。如果 A 的构造函数尚未运行,我们无法确定 A 是否已正确设置,因此调用它的任何方法都是一个非常糟糕的主意。

有一些解决方法,但这是different question 的主题。

【讨论】:

    【解决方案2】:

    如果方法只定义在Class A中,则无法通过Class B访问。

    如果Class AClass B中都存在该方法,则可以使用virtual方法和override

    【讨论】:

      【解决方案3】:

      认为最好在示例中使用名称 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();
              }
          }
      

      【讨论】:

        【解决方案4】:

        有两种方法可以解决这个问题。我先举个例子吧。

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

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

        【讨论】:

          猜你喜欢
          • 2011-03-05
          • 1970-01-01
          • 2019-03-02
          • 1970-01-01
          • 2010-10-08
          • 1970-01-01
          • 2019-07-08
          • 2011-01-05
          • 2014-02-16
          相关资源
          最近更新 更多