泛型防止虚函数调用?

时间:2020-08-17 14:15:48

标签: c# generics visual-studio-2019 vtable c#-8.0

多年后,我重新开始使用C#。我非常生锈,所以这可能不是我的编译器。

虚拟函数调用似乎像我期望的那样停止工作。这是代码:

namespace ConsoleApp1 {

    public abstract class Root {
        // force me to implenent ToString() in subclasses
        public abstract new string ToString();
    }

    public class Mammal : Root {
        string m;
        public Mammal(string mIn) { m = mIn; }
        public override string ToString() => m;
    }

    // allow me to give a mammal a name:
    public class NamedMammal : Root {
        Mammal m; string name;
        public NamedMammal(Mammal mIn, string nameIn) {
            m = mIn; name = nameIn;
        }
        public override string ToString() =>
            $"({m.ToString()} has name {name})";
    }

    // but I may want to name many things, so make 
    //the thing being named generic:
    public class NamedThing<Tthing> : Root {
        Tthing thing; string name;
        public NamedThing(Tthing thingIn, string nameIn) {
            thing = thingIn; name = nameIn;
        }
        public override string ToString() =>
            $"({thing.ToString()} has name {name})";
    }
    // I can name tractors or planets if I want to now

    class Program {
        static void Main() {

            Mammal c = new Mammal("cow");
            NamedMammal nm = new NamedMammal(c, "daisy");
            Console.WriteLine(nm.ToString());
            // output, as expected:
            // (cow has name daisy)
            
            Mammal f = new Mammal("fox");
            NamedThing<Mammal> nt = 
                new NamedThing<Mammal>(f, "freddie");
            Console.WriteLine(nt.ToString());
            // not what I expected:
            // (ConsoleApp1.Mammal has name freddie)
        }
    }
}

它显然不会丢失类型,因为它将其输出为“ ConsoleApp1.Mammal”,因此它知道它是哺乳动物,但它似乎并没有像我期望的那样调用ToString()。我猜想它正在调用最基础的对象ToString()而不是最基础的对象。

这是设计的吗,如果可以的话,有人可以给我一些指导,也许是关于如何处理此问题的想法,因为我不想手写这些样板。

1 个答案:

答案 0 :(得分:3)

问题在于方法object.ToString()Root.ToString()没有任何关联。这是您设计的。

在类NamedMammal中,您期望类型为Mammal事物实现Root。因此,编译器知道在给定实例上肯定存在方法Root.ToString()。然后,在NamedMammal.ToString()方法中,您将调用Root.ToString()的{​​{1}}实现,因为它正在遮盖(而不是覆盖)Mammal

但是对于object.ToString()类,您的事物不必实现NamedThing,因此在方法Root.ToString()中,调用NamedThing.ToString()将调用{{ 1}}。这不会导致调用thing.ToString()方法,因为它不是object.ToString()的重要方法。

对此的一种解决方案是调节Root.ToString()的类型参数以实现object.ToString(),例如:

NamedThing

另一种解决方案是将Root替换为

public class NamedThing<Tthing> : Root where Tthing : Root 
{ .. }

new

public abstract new string ToString();

通过这种方式,实现类型将覆盖override,而不是不相关的相似命名方法。


但是请注意,至少需要第一个解决方案来强制与public abstract override string ToString(); 一起使用的类型实现object.ToString()

相关问题