В чем разница между базовым ссылочным типом и производным ссылочным типом - PullRequest
0 голосов
/ 16 декабря 2011
class Dog
{
}
class BullDog : Dog
{
}
class Program
{
    static void Main()
    {
        Dog dog2 = new BullDog();
        BullDog dog3 = new BullDog();
    }
}
  1. В чем разница между использованием Dog в качестве эталона и BullDog в качестве эталона?

  2. У меня есть привычка использовать var dog3 = new BullDog();,который похож на BullDog dog2 = new BullDog();.Когда нам нужно использовать Dog dog2 = new BullDog();?

Ответы [ 6 ]

4 голосов
/ 16 декабря 2011

РЕДАКТИРОВАТЬ: Чтобы ответить на дополнительный вопрос из комментариев:

static void TakesDog(Dog theDog) { ... }

static void TakesBulldog(Bulldog theBulldog) { ... }

static void TakesObject(object theObject) { ... }

static void Main()
{
    //Given these declarations...
    object dog = new BullDog();
    Dog dog2 = new BullDog();
    BullDog dog3 = new BullDog();

    //These calls will work because a BullDog is a Dog:
    TakesDog(dog2);
    TakesDog(dog3);

    //this call will work because a Bulldog is a Bulldog:
    TakesBulldog(dog3);

    //and these calls will ALL work because all Dogs are Objects:
    TakesObject(dog);
    TakesObject(dog2);
    TakesObject(dog3);

    //However, these calls will fail because an Object or Dog is not 
    //necessarily a Bulldog,
    //EVEN THOUGH our current dog and dog2 are indeed references to Bulldogs:
    TakesBulldog(dog);        
    TakesBulldog(dog2);

    //An explicit conversion is necessary to make the above calls work:
    TakesBulldog(dog2 as Bulldog); //works given the current reference
    TakesBulldog((Bulldog)dog2); //works given the current reference
    TakesBulldog(dog as Bulldog); //ditto
    TakesBulldog((Bulldog)dog); //ditto

    //but if we change the value of dog2 to some other dog:
    dog2 = new Labrador();

    //the above calls now fail:
    TakesBulldog(dog2 as Bulldog); //passes null into the method
    TakesBulldog((Bulldog)dog2); //throws InvalidCastException

    //you can avoid problems by checking the true type:
    if(dog2 is Bulldog) //interrogates the type of the referenced object
       TakesBulldog((Bulldog)dog2); //works
    else
       TakesDog(dog2); //general fallback case

    //Object is similar but even more basic:
    dog = "I'm a dog"; //now dog is a System.String

    //this call still works:
    TakesObject(dog);

    //but these will fail:
    TakesDog(dog);
    TakesBulldog(dog);
}

Последнее, что нужно понять:

//given these declarations:
object dog = new BullDog();
BullDog dog2 = new BullDog();

//even though dog is a BullDog, attempting to call BullDog-specific 
//members (methods, properties, fields) will fail:
dog.Drool();

//you may only call members as specific as the object type of the 
//variable holding the reference:
dog.ToString(); //defined by Object. If you've overridden it in Dog or BullDog,
   //you'll get that implementation
dog2.Drool(); //works because we know from the variable that dog2 is a BullDog.
3 голосов
/ 16 декабря 2011

Обычно при использовании наследования вы также будете использовать перегрузку.

Но учтите следующее:

    static void Main()
    {
        Dog dog = new BullDog();
        BullDog bulldog = new BullDog();

        dog.Execute();
        bulldog.Execute();
    }

    class Dog
    {
        public virtual void Execute()
        {
            Console.WriteLine("Execute Dog");
        }
    }

    class BullDog : Dog
    {
        public new void Execute() // use new instead of override
        {
            Console.WriteLine("Execute BullDog");
        }
    }

Это напечатает:

Execute Dog
Execute BullDog

Вам необходимо определить подтип, если вы хотите получить доступ к функциям, которые доступны только для этого типа. Если вы хотите использовать перегрузку оператора так, как он предназначен (с помощью оператора переопределения), вы можете использовать это поведение подкласса, не беспокоясь о текущем типе.

- EDIT -

Вы спрашивали о разнице между:

object a3 = new BullDog();
BullDog a3 = new BullDog();

Ну, для начала, в той же области это даст вам ошибку компилятора, потому что a3 не может быть определен дважды. Но допустим, вы определяете их в разных областях.

Для объекта a3 вы можете вызывать только те методы, которые доступны для объекта (Equals, GetHashCode, ToString, GetType ()). Если вы хотите использовать методы, доступные только в классе Dog, вы должны привести его к Dog.

 object a3 = new BullDog();
 BullDog a4 = new BullDog();

 if (a3 is Dog)
 {
     // only executes when a3 is a Dog
     // a3 is for the compiler still of type object, so you can't call any other methods on it
 }

 Dog d1 = a3 as Dog; // cast it to a dog

 if (d1 != null) // if d1 == null, then a3 was not of type dog and the cast returned null
 {
     d1.Execute(); // we know now that d1 is a dog and that it points to a dog instance so we can call a dog method on it.
 }

 a4.Execute();
3 голосов
/ 16 декабря 2011

Когда вы используете базовый тип в качестве ссылочного типа, вы можете вызывать только те элементы, которые определены для базового типа.Вы должны привести к использованию членов супертипа.

Итак, если BullDog определил метод DoNotRelease, вы не могли бы вызвать его непосредственно из ссылки Dog.

В терминах var - это выведет более конкретный тип.Таким образом, при использовании new BullDog() выведенный тип будет BullDog.

1 голос
/ 16 декабря 2011

В: «В чем разница между использованием Dog в качестве ссылки и BullDog в качестве ссылки?»

A: Если у вас есть ссылка на Dog, любые дополнительные методы / свойства / поля, которые вы добавляете в производный тип BullDog, не будут общедоступными.

например. если у вас было:

public class Dog
{
    public virtual void Bark() 
    { 
        Console.WriteLine("Woof"); 
    }
} 

public class BullDog : Dog
{
            public override void Bark() 
    {
        Console.WriteLine("BOWF!"); 
    }

    public void Slobber() 
    {
        Console.WriteLine("I cannot control my drool :("); 
    }
{

... вы не сможете сделать это:

Dog baseDog = new BullDog();
baseDog.Slobber(); // error -- Dog doesn't know how to slobber.

... поскольку метод не существует для базового типа.

Кроме того, в зависимости от того, есть ли у вас базовая / производная ссылка, могут также возникнуть некоторые тонкие проблемы, которые могут возникнуть при небрежном использовании оператора new. Однако, по моему опыту, это очень редкие случаи (см. Пост Вутера де Корта, поскольку он только что опубликовал лучшее объяснение, когда я его печатал).

В: "У меня есть привычка использовать var dog3 = new BullDog (); это похоже на BullDog dog2 = new BullDog (); Когда нам нужно использовать Dog dog2 = new BullDog () ;?"

A: Вы можете даже не знать, какой тип Dog вы получаете, все, что вы знаете ... это Dog. Считай ...

public static class DogFactory
{
    public static Dog CreateMysteryDog()
    {
        return new Shitzu();
    }
}

Dog dog = DogFactory.CreateMysteryDog(); // what is the concrete type of Dog?  

DogFactory возвращает ссылку Dog, а не ссылку Shitzu, поэтому вы должны использовать базовый тип. В этом случае переменная тоже будет Dog, а не Shitzu.

1 голос
/ 16 декабря 2011

позволяет определить другой класс:

class OtherDog : Dog 
{

}

теперь вы можете определить список следующим образом:

C #:

List<Dog> Dogs = new List<Dog>();
Dogs.Add(new BullDog());
Dogs.Add(new OtherDog());

у вас есть список с типом Dog, но вы можете добавить к нему типы BullDog и OtherDog

это одно из названий предметов ООП "Полиморфизм"

ex2:

давайте подумаем, что вы хотите разработать краску:

class Shape ()
{

    virtual public void Draw()
    {
    }

}

class Rectangle : Shape ()
{

    override public void Draw()
    {
    }

}

class Circle : Shape ()
{

    override public void Draw()
    {
    }

}


void main ()
{

    List<Shape> Shapes = new List<Shape>();
    Shapes.Add(new Rectangle());
    Shapes.Add(new Circle());

    Shape[1].Draw(); //Draw a rectangle
    Shape[2].Draw(); // Draw a circle

}

если вам нужны подробности, прокомментируйте меня, чтобы отредактировать мой ответ для более подробной информации

Тханск, Али

1 голос
/ 16 декабря 2011

Вам не нужно Dog dog2 = new BullDog(), вы сможете передать свой BullDog любому методу, ожидающему Dog.

Взгляните на http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...