简体   繁体   中英

Returning derived class instance from a base class

The title may be unclear, but please look at the pattern below

public abstract class Animal
{

    public abstract Dog GetDog { get; }

    public abstract Cat GetCat { get; }

}

public class Dog : Animal
{

    public override Dog GetDog {
        get { return this; }
    }

    public override Cat GetCat {
        get { return null; }
    }

}

Is this considered a bad practice to have properties in the base class, return derived types. Or should I do something like

public abstract AnimalTypeEnum AnimalType { get; }

EDIT: Based on the comments, I guess I should be more clear on what I am trying to achieve. A new instance of the Dog or Cat class would be created by a separate function based on certain criteria and would then return Animal type to the caller. The calling method would check the type of the returned instance and use it accordingly.

public Animal CreateAnimal(string path)
{

    //Process the document in path and create either a new instance of dog or cat

    Dog dg = new Dog();

    return dg;

}

If you only want the animal you are deriving from you can do this:

public abstract class Animal<T> where T: Animal<T>
{
    public T GetAnimal 
    {
        get { return (T)this; }
    }
}

public class Dog : Animal<Dog>
{
}

public class Cat : Animal<Cat>
{
}

public class Giraffe : Animal<Giraffe>
{
}

You call this way:

var cat = new Cat();
var dog = new Dog();
var giraffe = new Giraffe();
Cat cat2 = cat.GetAnimal;
Dog dog2 = dog.GetAnimal;
Giraffe giraffe2 = giraffe.GetAnimal;

The calling method would check the type of the returned instance and use it accordingly.

There's your problem. It's code smell to need to do that. You should be able to treat whatever it is as just an object, rather than treating dogs and cats differently.

If you need to display the content of either animal, then override the ToString method on both classes and just call ToString on animal. If you need to know the name of the dog or cat then add a Name property to Animal . If at all possible you should be using polymorphism here so that whatever is using the object treads it as just an Animal and simply involves different things happening as a result of different implementations of the same method.

If you really, really do need to know whether the Animal is a Dog or a Cat then you can use the is or as operators; you don't need to add all of the code that you've shown in the OP.

Nah you are doing it wrong.

The better thing would be to have a single method.

public abstract Animal getAnimal();

Any derived class will then know how to return itself. I hope that makes sense. But then I don't think you would want to return an Animal either. Doesn't make sense.

Animal dog = new Dog() ;
dog.getAnimal(); 

confusing right?

You could have an array of Animals/List, iterate through the collection and check like so:

if(animal is Dog)

But still you are checking the type. If you want to use a base class have it so that it makes sense and exposes a common method.

This is a really bas design since it violates the Open-Closed principle . You want your classes to be open for extension but closed to modification. What will happen if tomorrow you'll want to add another class?

public class Donkey : Animal
{
}

You will have to go and change the base class so it will have a property GetDonkey . You will also have to go and change all the classes that use your class and add if (animal.GetDonkey == null)

What you should do is use a factory design pattern like this:

public static class AnimalFactory
{
  public static Dog GetDog()
  {
    return new Dog();
  }

  public static Cat GetCat()
  {
    return new Cat();
  }
}

or you should use a virtual method like @Lews Therin suggested.

It doesn't seem very useful to have properties that just return the object they're called on.

Maybe you're just looking for a way to downcast from a superclass to a subclass? If so, just use casts:

Animal a = new Cat();

try
{
    Dog d = (Dog)a;
}
catch (InvalidCastException)
{
    try
    {
        Cat c = (Cat)a;
    }
    catch (InvalidCastException)
    {
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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