基准引用类型与派生引用类型之间有什么区别

时间:2011-12-16 18:24:17

标签: c# c#-4.0

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 个答案:

答案 0 :(得分:4)

编辑:要解决评论中的其他问题:

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.

答案 1 :(得分:3)

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

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

var而言,它会推断出更具体的类型。因此,如果使用new BullDog(),推断类型将为BullDog

答案 2 :(得分:3)

通常在使用继承时,您还将使用重载。

但请考虑以下因素:

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

让我们定义另一个类:

class OtherDog : Dog 
{

}

现在您可以定义如下列表:

C#:

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

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

这是OOP主题之一,名称为“Polymorphism”

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

}

如果您需要更多详细信息,请评论我编辑我的回复以获取更多详细信息

thansk,阿里

答案 5 :(得分:1)

问:“使用Dog作为参考与BullDog作为参考有什么区别?”

答:如果您有Dog引用,则您添加到派生类型BullDog的任何其他方法/属性/字段将无法公开访问。

E.g。如果你有:

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的帖子,因为他刚刚在我输入时发布了更好的解释)。

问:“我习惯使用var dog3 = new BullDog();这类似于BullDog dog2 = new BullDog();我们什么时候需要使用Dog dog2 = new BullDog();?”

答:你甚至可能不知道你得到了什么类型的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