我为什么要创建一个具有基类引用的对象?

时间:2016-05-31 16:22:48

标签: c# oop inheritance polymorphism

    public class BaseClass
    {
        public virtual void Display()
        {
            Console.WriteLine("I am Base Class");
        }

        public void BaseClassMethod()
        {
            Console.WriteLine("I am Base Class Method");
        }
    }

    public class DerivedClass : BaseClass
    {
        public override void Display()
        {
            Console.WriteLine("I am Derived Class");
        }

        public void DerivedClassMethod()
        {
            Console.WriteLine("I am Derived Class Method");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            BaseClass bc = new BaseClass();
            bc.Display();
            bc.BaseClassMethod();

            Console.WriteLine("--------------");

            DerivedClass dc = new DerivedClass();
            dc.Display();
            dc.BaseClassMethod();
            dc.DerivedClassMethod();

            Console.WriteLine("--------------");

            BaseClass bc2 = new DerivedClass();
            bc2.Display();
            bc2.BaseClassMethod();
            //bc2.DerivedClass();  --> I can't reach b2.DerivedClass() method

            Console.ReadLine();
        }
    }

大家好。我试图清楚我的思路为什么以及我想在哪里创建和使用基类引用中的派生类对象。我知道虚拟是如何工作的。我知道派生类是一个基类,我可以覆盖虚方法。我可以在基类中访问非虚方法。但我想知道哪里可以,为什么我要使用这种对象创建方式。就像我在示例代码的最后一部分一样;

BaseClass bc2 = new DerivedClass();

我无法访问派生类方法,所以我无法使用派生类方法。但由于新的DerivedClass,它仍然是派生类。如果我使用普通的DerivedClass d = new DerivedClass();风格,我可以使用两种类方法。我只是找不到任何理由和情况我想用这种风格。如果有人告诉我在哪种情况下我必须使用基类引用中的派生类对象,我会很高兴所以我可以理解这种风格存在于语言中。我想知道为什么,我不是在问为什么这不起作用或类似的东西。只是想知道情况。谢谢。

5 个答案:

答案 0 :(得分:2)

有两个主要用法:

1)多种类型的集合

让我们稍微改变你的例子

public class Shape
{
    public virtual void Display()
    {
        Console.WriteLine("I am a Shape");
    }

    public void BaseClassMethod()
    {
        Console.WriteLine("I am Base Class Method");
    }
}

public class Square : Shape
{
    public override void Display()
    {
        Console.WriteLine("I am Square");
    }

    public void DerivedClassMethod()
    {
        Console.WriteLine("I am Derived Class Method");
    }
}



public class Circle : Shape
{
    public override void Display()
    {
        Console.WriteLine("I am Circle");
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Shape> shapes = new List<Shape();
        shapes.Add(new Square());
        shapes.Add(new Circle());

我有一个列表,可以在一个集合中保存Circles,Squares和generic Shapes。

2)多态性

继续上一段代码

         foreach(Shape shape in shapes)
         {
             shape.Display();
         }

我们不知道变量Shape是什么类型shape,但我们知道无论它是什么类型,我们都可以使用Display()方法调用它会显示正确的信息。

多态性在你需要调用一个函数时非常有用,但是你不知道某些东西的特定类型,因为你正在拉一些像上面这样的基类型,或者你想编写一个函数可以接受任何类型的Shape,因为该功能不需要知道具体的类型来完成它的工作。

    public static void LogDisplay(Shape shape)
    {
         Console.WriteLine("I am about to call shape.Display()");
         shape.Display();
         Console.WriteLine("I am just called shape.Display()");
    }

答案 1 :(得分:2)

我最喜欢的例子,因为人们可以理解使用,是记录。想象一下,我创建了一个网站。当我开发网站时,我想登录到我的文件系统,因为它很容易访问。当我部署网站时,我想登录到事件日志,因为我可能无法直接访问该计算机上的文件系统。

但是,我只想更改记录的位置,我希望基类构建实际文本的外观。所以我有我的基类格式化文本:

public abstract class BaseLogger
{
     public abstract void LogException(Exception ex);
     public abstract void LogUserMessage(string userMessage);

     protected string GetStringFromException(Exception ex)
     {
        //....
     }

     protected string GetStringFromUserMessage(string userMessage)
     {
        //....  
     }
}

现在我可以拥有一个记录到文件系统的类:

public class FileLogger : BaseLogger
{
    public FileLogger(string filename)
    {
        //initialize the file, etc
    }

    public override void LogException(Exception ex)
    {
        var string = GetStringFromException(ex);
        File.WriteAllLines(...);
    }

    public override void LogException(Exception ex)
    {
        var string = GetStringFromUserMessage(ex);
        File.WriteAllLines(...);
    }
}

和我的类登录到事件日志:

public class EventLogger : BaseLogger
{  
    public EventLogger()
    {
        //initialize the eventlog, etc
    }

    public override void LogException(Exception ex)
    {
        var string = GetStringFromException(ex);
        EventLog.WriteEntry(...);
    }

    public override void LogException(Exception ex)
    {
        var string = GetStringFromUserMessage(ex);
        EventLog.WriteEntry(...);
    }
}

现在在我的课程中,我只关心我在课程中注入一个BaseLogger。实现细节无关紧要,我只知道无论我使用什么,我都可以LogExceptionLogUserMessage

当我使用记录器时,我受益于不关心我使用的派生类。这就是将每个派生类视为基类的好处。我可以在没有程序关怀的情况下将它们换掉。

答案 2 :(得分:1)

这样做的原因有很多,主要与代码可重用性和可扩展性有关,换句话说,即可轻松进行小的更改或增强,而无需重写整个代码。

一个真实世界的例子(经常发生)是你有不同的客户使用你的软件,可能需要你支持不同的数据库(甚至不同的表结构)。因此,为了做到这一点,您可以从公共基类派生实现,并在实现细节上有所不同,而不会影响程序的其余部分。

  

这也遵循设计原则“程序   到'接口',而不是GoF design pattern book

中解释的'实现'
public abstract class ProviderBase
{
    public abstract Employee[] GetAllEmployees();
}

public class MySqlProvider:ProviderBase
{
    public override Employee[] GetAllEmployees()
    {
        string select = "select * from employees";

        //query from mysql ...
    }

}

public class MsSqlProvider : ProviderBase
{
    public override Employee[] GetAllEmployees()
    {
        string select = "select * from user u left join employee_record e on u.id=e.id";

        //query from mysql ...
    }
}

然后在主程序中,您可以通过配置或Dependency Injection

更改数据库实现的类型
ProviderBase provider = null;
if(databaseType == "MySql")
{
    provider = new MySqlProvider();
}
else if (databaseType == "MsSql")
{
    provider = new MsSqlProvider();
}

var employees = provider.GetAllEmployees();
//do something

答案 3 :(得分:0)

我相信使用派生类的可用性背后的许多原因都与最小化重复代码有关。

引用现实生活中的例子...... 如果我要求你描述汽车的属性和能力,然后是要求你为电动汽车做同样的事情,你会发现两者都有很多属性和能力。因此,不是将它作为两个完全独立的类,而是创建基类Car,并从中导出electricCar会更有效。那么你只需要考虑派生类中电动车的具体差异,并且所有共享的属性和能力都将继续存在。

希望这有助于您了解基类和派生类的有用性。非常简单,但我觉得它可以帮助你掌握这个概念!

答案 4 :(得分:0)

使用基类的主要原因是可重用性多态

所以你可以根据条件创建类:

BaseClass bc
if(case1)
    bc = new DerivedClass1();
else
    bc = new DerivedClass2();

在以下应用程序中,即使您不知道编译时它是什么类的派生类,也可以使用bc。你可以传递它,例如到其他函数并调用重写的方法:

bc.Display();

派生类方法只有在您知道实际具有哪种派生类时才能使用。然后你可以进行转换。

DerivedClass1 dc = bc as DerivedClass1;
dc.DerivedClassMethod()