基引用类型与派生引用类型的区别是什么?

本文关键字:引用类型 是什么 区别 派生 | 更新日期: 2023-09-27 18:18:00

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();

基引用类型与派生引用类型的区别是什么?

EDIT:解决评论中的其他问题:

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.

使用基类型作为引用类型时,只能调用在基类型上定义的成员。为了使用超类型的成员,需要进行强制类型转换。

因此,如果BullDog定义了DoNotRelease方法,则不能直接从Dog引用调用它。

对于var -,它将推断出更具体的类型。因此,如果使用new BullDog(),则推断类型将是BullDog

通常在使用继承时也会使用重载。

但是考虑以下内容:

    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

如果要访问仅对该类型可用的函数,则需要定义子类型。如果你想使用操作符重载(使用覆盖操作符),你可以使用这个子类的行为,而不用担心当前的类型。

——编辑——

你问的是:

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();

您不需要Dog dog2 = new BullDog(),您将能够将BullDog传递给任何期望Dog的方法。

看看http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming

让我们定义另一个类:

class OtherDog : Dog 
{
}

现在你可以这样定义一个列表:

c#:

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

你有一个Dog类型的列表,但是可以添加BullDog和OtherDog类型的列表

这是其中一个名为"多态性"的OOP主题

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
}

如果你需要更多的细节,请评论我编辑我的回复以获得更多的细节

谢谢你,Ali

问:"使用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操作符,还会出现一些微妙的问题,这取决于您是否有基/派生引用。然而,这些在我的经验中是非常罕见的(见Wouter de Kort的帖子,因为他在我打字的时候发布了一个更好的解释)。

Q:"我习惯使用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引用,所以你必须使用基类型。在这种情况下,var也将是Dog而不是Shitzu