lom*_*do2 7 c# oop polymorphism
我是OOP的新手,多态性让我很难过:
class Animal
{
public virtual void eat()
{
Console.Write("Animal eating");
}
}
class Dog : Animal
{
public override void eat()
{
Console.Write("Dog eating");
}
}
class Program
{
public void Main()
{
Animal dog = new Dog();
Animal generic = new Animal();
dog.eat();
generic.eat();
}
}
Run Code Online (Sandbox Code Playgroud)
这样打印
Dog eating
Animal eating
Run Code Online (Sandbox Code Playgroud)
但是为什么不使用Dog类型代替动物,比如Dog dog = new Dog()?当你知道这个物体是一种动物,但我不知道它是什么样的动物时,我认为这很方便.请向我解释一下.
谢谢
您可以通过其超类引用子类.
Animal dog = new Dog();
Animal cat = new Cat();
Animal frog = new Frog();
List<Animal> animals = new List<Animal>();
animals.add(dog);
animals.add(cat);
animals.add(frog);
foreach(Animal animal in animals)
{
Console.WriteLine(animal.eat());
}
Run Code Online (Sandbox Code Playgroud)
当你想要一些不关心具体实现的一般方法,而只关注总体类型时,多态性真的很方便.使用你的动物例子:
public static void Main()
{
var animals = new List<Animal>();
animals.Add(new Dog());
animals.Add(new Cat());
foreach (var animal in animals)
Feed(animal);
}
public static void Feed(Animal animal)
{
animal.Eat();
}
Run Code Online (Sandbox Code Playgroud)
请注意,该方法并不关心它是什么类型的动物,它只是试图喂它.也许这样可以Dog
实现Eat()
吞噬视线中的一切.也许Cat()
实施它,它咬了一口,走开了.也许Fish()
它实现它吃得太多而且死了.该方法本身并不关心Animal
它得到了什么,您可以轻松添加更多Animal
类型,而无需更改接受它们的方法.
(与此相关的是战略模式.)
相反,有时您需要一种方法来返回一般类型,而不管实现了什么.我使用的一个常见例子是:
public interface AnimalRepository
{
IEnumerable<Animal> GetAnimals();
}
Run Code Online (Sandbox Code Playgroud)
实际上,这以两种方式使用多态.首先,Animal
它返回的s 的枚举可以是任何类型.在这种情况下,任何调用代码都不关心哪一个,它将以更一般的方式使用它们(例如在前面的例子中).此外,IEnumerable
可以返回任何实现的东西.
所以,例如,我有一个使用LINQ to SQL的接口的实现:
public class AnimalRepositoryImplementation : AnimalRepository
{
public IEnumerable<Animal> GetAnimals()
{
return new DBContext().Animals;
}
}
Run Code Online (Sandbox Code Playgroud)
这会返回一个IQueryable
.然而,无论什么称呼这种方法,都不关心它是什么IQueryable
.它只会使用这些功能IEnumerable
.
或者,我有另一个模拟测试实现:
public class AnimalRepositoryImplementation : AnimalRepository
{
private IList<Animal> animals = new List<Animal>();
public IEnumerable<Animal> GetAnimals()
{
return animals;
}
}
Run Code Online (Sandbox Code Playgroud)
这是返回一个IList
,它再次被多态化为更一般,IEnumerable
因为这是所有调用代码将使用.
这些也被称为协方差和逆变.在返回IEnumerable
上面的类型的情况下,类型从更具体(IQueryable
和IList
)移动到更通用(IEnumerable
).他们能够在没有转换的情况下执行此操作,因为更具体的类型也是类型层次结构中更通用类型的实例.
与此相关的还有Liskov替换原则,该原则声明类型的任何子类型都可以用作该父类型,而无需对程序进行更改.也就是说,如果Dog
是的子类型Animal
,那么你应该总是能够用Dog
作为Animal
,而不必知道它是一个Dog
或作出任何特殊考虑它.
您也可以从查看依赖性倒置原则中受益,上述存储库实现可以作为示例.正在运行的应用程序不关心哪个type(AnimalRepositoryImplementation
)实现接口.它关心的唯一类型是界面本身.实现类型可以具有附加的公共或至少内部方法,实现组件使用这些方法来实现如何实现特定依赖性,但这些方法对于消费代码没有任何影响.每个实现都可以随意交换,调用代码只需要提供更通用接口的任何实例.
旁注:我个人发现继承经常被过度使用,特别是普通的继承,就像Animal
示例中的Animal
本身不应该是可实例化的类一样.如果应用程序的逻辑需要通用形式,它可以是接口或抽象类.但不要仅仅是为了做到这一点.
一般来说,根据Gang Of Four一书的推荐,更喜欢构图而不是继承.(如果您没有副本,请获取副本.)不要过度使用继承,但请在适当的地方使用它.如果将常用功能分组到组件中并且每个组件都是由这些组件构建的,那么应用程序可能更有意义吗?当然,更常用的例子可以从中吸取教训.Animal
Animal
Car
保持逻辑上定义的类型.你应该写new Animal()
吗?拥有一个Animal
不再具体的通用实例是否有意义?当然不是.但是,拥有应该能够在任何Animal
(进料,复制,模具等)上运行的通用功能可能是有意义的.