2012-01-31 159 views
1

这是一个很难解释的问题,但我会尽我所能。我已经实例化了一个存储在A类型变量中的类型为B的对象。我使用get类型属性,所以现在它是类型B.因此,我执行了A和B类型的隐式转换。 show()被调用是它的类型B?C#类型转换

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication1 
{ 

    class A 
    { 
     public virtual void show() 
     { 
      Console.WriteLine("Showing A"); 
     } 

     public void test() 
     { 
      Console.WriteLine("called from A"); 
     } 
    } 

    class B:A 
    { 
     public override void show() 

     { 
      Console.WriteLine("Showing B"); 
     } 

     public void testb() 
     { 
      Console.WriteLine("called from B"); 
     } 
    } 
    class Program 
    { 
     static void Main(string[] args) 
     { 


      A a = new B(); 
      // Outputs ConsoleApplication.B 
      Console.WriteLine("{0}", a.GetType()); 
      // outputs showing B 
      a.show(); 
      // outputs called from A 
      a.test(); 

      Console.ReadLine(); 
     } 
    } 
} 
+1

您是否知道您命名方法testb?这可能是一个错字。 – Joe 2012-01-31 21:45:43

回答

3

,你有没有真正你还没有执行的隐式转换转换任何东西

A a = new B(); 

您已直接实例化类型B的对象可以从类型A的变量引用它,因为B是亚类A的。然而,这两个动作应被视为独立的。

... = new B();不管左边是什么,

将始终instanciate类型B的对象。

A a = ... 

将始终尝试将右侧的内容分配给变量a。

引用类型的唯一区别在于CLR如何查看对象。

A a = new B(); 
B b = new B(); 

两者都是B型的对象。然而,“A”,只会让你使用它,仿佛它是A型的对象,因此,你只能叫a.show()和.test(),即使它实际上是B类型,所以也有附加方法testb()。 'b'将允许你调用b.show(),b.test()和b.testb()。无论变量类型如何,当您调用show()时,它都是b类型,因此它是被返回的被覆盖的show()方法。

最后,你现在可以做的是沮丧。即你实例化了一个B类型的对象,所以你现在可以将它转换为一个类型为b的变量,因此允许完全访问所有类型为B的成员。

例如为:

A a = new B(); 
a.testb(); // this will not compile as a does not have a definition of testb(). 

A a = new B(); 
B b = (B)a; // downcast a to a reference of type B 
b.testb(); // this is now fine 

我刚才看见你的,为什么你要实例化的东西A型附加的问题,即基类。这是为了提供一种共同点,作为处理不同类型对象的一种方式。例如,你可能有一个基类动物,并从它的几个子类 - 猫,狗,沙鼠。但是,您可能希望能够将所有动物的列表传递给只需要使用公共属性的方法。因此,如果Animal有一个名字,即使Animal本身可以是Abstract(不能实例化),并且该列表完全由Dogs,Cats和Gerbils组成,但是您的List可以迭代并引用每个动物名称。然后,您还可以通过找出哪些类型与其进行进一步交互来对各种动物进行下注,例如,

if (animal is Dog) { 
    Dog dog = (Dog)animal; 
} 

或者你可以使用 '为' 关键字做演员:

Dog dog = animal as Dog; 

希望这有助于。

+0

谢谢,很好的解释。 – 2012-01-31 22:28:05

+0

Simon是正确的(就像我一样),但是如果你发现自己不得不贬低A到B来使用它,很可能你已经打破了[Liskov Substitution Principle](http://en.wikipedia。组织/维基/ Liskov_substitution_principle)。 – 2012-01-31 22:37:07

0

可变aB类型的实例,因此没有转换,以便需要a.show()B.show()方法。这正是虚拟方法应该做的事情:调用变量引用的实例化类型(或继承链中的第一个实现)上的方法,而不是变量的声明类型。

a.test()的调用不会这样做,因为该方法未声明为virtual。所以它简单地称为A.test()方法。

2

仅仅因为您将它存储在类型A的变量中,您的变量仍会引用类型B的对象。当您对变量“a”进行调用时,CLR知道调用的正确方法。既然你重载了show方法,那么类型B的show方法将被调用。但是你没有重写test(),因此类型A的方法实现被调用。这是可能的,因为B“从A继承了test()行为,但并未覆盖它。

+0

所以这个变量是A类型的,那么它是如何提供给B类型的对象引用的呢?我不能只做B型变量,它会执行相同的操作。如果是这样的话,为什么你会想要制作A型的变量? – 2012-01-31 22:13:29

+0

是的,如果你改变了你的代码行,并且运行你的代码,它会运行完全一样的。有很多地方你可能想使用A而不是B。让我们假装你有一个“公共抽象类Shape”,并且你有'public class Circle:Shape'和'public class Square:Shape'。在你的Shape基类上,你可能有一个抽象方法'public abstract void Draw(Graphics g)'。您可以随身携带一个'List '和一个'List ',或者您可以简单地拥有一个包含Circle和Square实例的List 。你有看到? – 2012-01-31 22:33:49

1

在您的代码:

// outputs showing B 
    a.show(); 

打印 “显示B”,因为show()是一个虚拟的方法。任何虚拟方法调用都由对象类型决定,而不是参考类型(后期绑定)。参考a指向B类型的对象。

// outputs called from A 
    a.test(); 

test()简单地从A类,而不是一个虚拟方法继承的,因此它打印“从A称为”。方法调用a.GetType()不转换任何东西,它只是返回一个Type对象的实例。

0

正如你已经创建了B类的一个实例,其他职位提及,但因为您播的A型对象您已经为A型定义的接口

如果需要访问,为定义的方法B型,您可以向下转换的对象到它的原始类型,让你,例如用于B型

的定义:

if (a is B) 
{ 
    ((B)a).testb(); 
} 

// or 

B b = a as B; 
if (b != null) 
{ 
    b.testb(); 
}