C# 更多关于虚拟/新…以及接口的信息!

C# 更多关于虚拟/新…以及接口的信息!,c#,interface,virtual,overriding,new-operator,C#,Interface,Virtual,Overriding,New Operator,昨天我发布了一个关于new/virtual/override关键字的问题,我从你的回答中学到了很多。但我仍然有些怀疑 在所有“框”之间,我与类型的方法表的实际情况失去了联系。例如: interface I1 { void Draw(); } interface I2 { void Draw(); } class A : I1, I2 { public void Minstance() { Console.WriteLine("A::MInstance"); } public v

昨天我发布了一个关于new/virtual/override关键字的问题,我从你的回答中学到了很多。但我仍然有些怀疑

在所有“框”之间,我与类型的方法表的实际情况失去了联系。例如:

interface I1 { void Draw(); }
interface I2 { void Draw(); }

class A : I1, I2
{
    public void Minstance() { Console.WriteLine("A::MInstance"); }
    public virtual void Draw() { Console.WriteLine("A::Draw"); }
    void I2.Draw() { Console.WriteLine("A::I2.Draw"); }
}
class B : A, I1, I2
{
    public new virtual void Draw() { Console.WriteLine("B::Draw"); }
    void I1.Draw() { Console.WriteLine("B::I1.Draw"); }
}

class Test
{

    public static void Main()
    {
        A a = new B();
        a.Draw();
        I1 i1 = new A();
        i1.Draw();
        I2 i2 = new B();
        i2.Draw();
        B b = (B)a;
        b.Draw();
    }

}
}
本练习提出的问题是:根据代码填写类型的方法表,并解释运行Main()生成的输出

我的回答是: 在类型A中,我们有3个方法:MinInstance()、Draw()——A::Draw版本——和I2::Draw 在类型B中,我们有4个方法:来自A的MinInstance、B::Draw、I1::Draw和I2::Draw

我对我的答案不是很有信心,这就是为什么我要发布这个问题。当我们实现接口时,它在方法表上为所述接口的方法创建了一个新槽?我们不应该在类A中实现I2::Draw吗

同样,当我们使用接口变量(如i1.Draw())调用方法时,我知道我们处于动态调度状态,因此我们应该查看变量所持有的对象的类型(在这种情况下为类型a),并在a的方法表中搜索一个名为i1.Draw的方法。但是如果我们找不到呢?在这些情况下,我应该如何处理?为了成功地解决这些问题,我应该知道什么经验法则吗

对不起,这个问题太无聊了,但我真的需要解开我头上的这个结;)


干杯

据我所知,您在问,给定一个具有一些重写方法的子类,如何知道将调用哪个方法

基本上,我可以想到三种选择:

  • 若子类并没有在基类上定义方法,那个么基类的方法将被调用
  • 如果子类重写基类上的方法,则子类的方法将被调用
  • 如果子类定义了一个也存在于基类上的新方法,那么要调用的方法将取决于对对象的引用(无论您的变量类型是基类还是子类)
  • 我希望我理解你的问题

    编辑:关于接口的简要说明:如果基类实现了IInterface,那么派生自基类的子类已经实现了IInterface,并且不需要重新实现它。例如,如果有一辆IVehicle具有MPH属性,并且有一辆BaseVehicle实现了该属性,因为Car源于BaseVehicle,Car已经有了MPH属性“方法表”?不,这只是一份必须履行的合同。
    I1
    I2
    的契约可以用相同的
    Draw
    方法来满足,并且将会满足,除非您使用隐式实现将它们分开,这里就是这种情况。如果没有这一点,一个
    绘图
    方法就可以了


    在所有情况下,公共
    绘图
    将被调用,除非引用被转换为显式实现的接口类型。

    除了其他答案之外,我发布了一个正确的答案:

                A a = new B();
                a.Draw(); //A::Draw
    
                I1 i1 = new A();
                i1.Draw(); //A::Draw
    
                I2 i2 = new B();
                i2.Draw();// B::Draw
    
                B b = (B) a;
                b.Draw();// B::Draw
    
    注释和参考:我必须回到标准(ECMA-334)中,在§20.4.4接口重新实现中可以找到: 允许继承接口实现的类通过将接口包含在基类列表中来重新实现接口

    接口的重新实现遵循与接口的初始实现完全相同的接口映射规则。因此,继承的接口映射对为重新实现接口而建立的接口映射没有任何影响。[示例:在声明中]

    Control
    IControl.Paint
    映射到
    Control.IControl.Paint
    上的事实并不影响在
    MyControl
    中重新实现,它将
    IControl.Paint
    映射到
    MyControl.Paint

    继承的公共成员声明和继承的显式接口成员声明参与重新实现接口的接口映射过程。[示例:

    这里,
    Derived
    IMethods
    的实现将接口方法映射到
    Derived.F
    Base.IMethods.G
    Derived.IMethods.H
    Base.I
    。结束示例]

    当一个类实现一个接口时,它还隐式地实现该接口的所有基接口。同样,接口的重新实现也隐含着接口的所有基本接口的重新实现。[示例:

    这里,
    IDerived
    的重新实现也重新实现了
    IBase
    ,将
    IBase.F
    映射到
    D.F
    。结束示例]

    好问题

    考虑这一点的方法是:接口有自己的插槽集。需要一个实现接口的类来填充这些插槽

    • 接口I1有一个插槽,我们称之为I1SLOT
    • 接口I1有一个插槽,我们称之为I2SLOT
    • A类有自己的两个插槽,AMinSLOT和ADRAWSOT
    • 类有三个方法,我们称之为AMinMethod、ADrawMethod和AI2DrawMethod
    • 当您说“newa”时,运行时有四个插槽需要填充
    • I1SLOT用ADrawMethod填充
    • I2SLOT用AI2DRAW方法填充
    • AMinSLOT用AMinMethod填充
    • ADrawSLOT用ADrawMethod填充
    • B班有三个座位。它继承了AMinSLOT和ADrawSLOT,并定义了一个新的插槽BDrawSLOT
    • 类B有两个方法,BDrawMethod和BI1DrawMethod
    • 当你说“new B”时,运行时有五个插槽需要填充
    • I1SLOT用BI1Draw方法填写
    • I2SLOT用BDrawMethod填充
    • AMinSLOT用AMinMethod填充
    • ADrawSLOT用ADrawMethod填充
    • BDrawSLOT用BDrawMethod填充。interface I1 { void Draw(); } interface I2 { void Draw(); } class A : I1, I2 { // this is just a method in A public void Minstance() { Console.WriteLine("A::MInstance"); } // method in A, also implements I1.Draw. May be overridden in // derived types. public virtual void Draw() { Console.WriteLine("A::Draw"); } // implements I2.Draw, accessible on object a of type A via ((I2)a).Draw() void I2.Draw() { Console.WriteLine("A::I2.Draw"); } } class B : A, I1, I2 { // new method in B, does not override A.Draw, so A.Draw is only // callable on an object b of type B via ((A)b).Draw(). Types // derived from B may override this method, but can't override // A.Draw because it's hidden. Also implements I2.Draw (see notes). public new virtual void Draw() { Console.WriteLine("B::Draw"); } // implements I1.Draw, accessible on object b of type B via ((I1)b).Draw() void I1.Draw() { Console.WriteLine("B::I2.Draw"); } }
    interface IControl
    {
        void Paint();
    }
    class Control: IControl
    {
        void IControl.Paint() {…}
    }
    class MyControl: Control, IControl
    {
        public void Paint() {}
    }
    
    interface IMethods
    {
        void F();
        void G();
        void H();
        void I();
    }
    class Base: IMethods
    {
        void IMethods.F() {}
        void IMethods.G() {}
        public void H() {}
        public void I() {}
    }
    class Derived: Base, IMethods
    {
        public void F() {}
        void IMethods.H() {}
    }
    
    interface IBase
    {
        void F();
    }
    interface IDerived: IBase
    {
        void G();
    }
    class C: IDerived
    {
        void IBase.F() {…}
        void IDerived.G() {…}
    }
    class D: C, IDerived
    {
        public void F() {…}
        public void G() {…}
    }
    
    A a1 = new A();
    A a2 = new B();
    B b = new B();
    (a1 as A).Draw();  // ADrawSLOT contains A::Draw
    (a1 as I1).Draw(); // I1SLOT    contains A::Draw
    (a1 as I2).Draw(); // I2SLOT    contains A::I2.Draw
    (a2 as A).Draw();  // ADrawSLOT contains A::Draw
    (a2 as B).Draw();  // BDrawSLOT contains B::Draw
    (a2 as I1).Draw(); // I1SLOT    contains B::I1.Draw
    (a2 as I2).Draw(); // I2SLOT    contains B::Draw
    (b as A).Draw();   // ADrawSLOT contains A::Draw
    (b as B).Draw();   // BDrawSLOT contains B::Draw
    (b as I1).Draw();  // I1SLOT    contains B::I1Draw
    (b as I2).Draw();  // I2SLOT    contains B::Draw
    
    1 == 0:TypeDef[2000004], 1:MethodDefOrRef[06000005], 2:MethodDefOrRef[06000002]
    2 == 0:TypeDef[2000005], 1:MethodDefOrRef[06000008], 2:MethodDefOrRef[06000001]
    
    in type A the method A::I2.Draw implements the method I2::Draw
    in type B the method B::I1.Draw implements the method I1::Draw
    
    A a2= new B() (creates a new object using constructor of B, because b is of type A, therefore it works )
    and a2.draw() will result in execution of A class draw because the type is A.