什么时候确定泛型类型?它能被影响吗?

8

我一直在尝试使用泛型,并遇到了一些奇怪的问题。希望你们能给出解释!为了让事情更简单,我把“问题”放到了一个例子中:

namespace Lab
{
    public class Animal
    {
        public Animal(string sound)
        {
            this.Sound = sound;
        }

        public string Sound { get; private set; }

        public void Kick()
        {
            Printer.Print(this, Sound);
        }
    }

    public class Dog : Animal
    {
        public Dog() : base("Bark, bark! I'll bite you!") { }
    }

    public class Printer
    {
        public static void Print<T>(T obj, string message)
        {
            System.Console.WriteLine("{0} says '{1}' \n", typeof(T).FullName.PadRight(10), message);
        }
    }

    public static class Program
    {
        static void Main(string[] args)
        {
            Animal bird = new Animal("Tweet!");
            Dog dog = new Dog();

            System.Console.WriteLine("Kick bird:");
            bird.Kick();
            System.Console.WriteLine("Kick dog:");
            dog.Kick();
            System.Console.WriteLine("Print kick dog:");
            Printer.Print(dog, dog.Sound);

            System.Console.ReadLine();
        }
    }
}

所以,我的实验室里有两种动物:一只狗和一只鸟。当我“踢”这些动物时,它们会发出声音。打印机会打印出声音和动物的种类。运行程序后会打印以下内容:
Kick bird: Lab.Animal says 'Tweet!'
Kick dog: Lab.Animal says 'Bark, bark! I'll bite you!'
Print kick dog: Lab.Dog says 'Bark, bark! I'll bite you!'
为什么第一次踢狗会告诉我它是类型为Lab.Animal的狗?而且...我怎么才能让它返回Lab.Dog
2个回答

6

狗的第一脚告诉你类型参数的编译时类型是Lab.Animal。换句话说,你的Animal.Kick方法实际上是:

Printer.Print<Animal>(this, Sound);

类型参数不是通过多态来确定的,而是在编译时确定的。当一个调用的类型参数实际上是调用上下文的类型参数时,情况会变得更加复杂,但本质上仍然是相同类型的事情。

要使它显示为Lab.Dog,你必须获得对象的实际执行时类型,例如使用:

obj.GetType().FullName

我的问题是GetType()相对于typeof()来说比较耗费资源。但这并不是例子的重点。感谢解释! - Kees C. Bakker
@Kees:它之所以“昂贵”,正是因为它在执行时而不是编译时进行某些操作,但你可以使用你需要的东西 :) (不过,它真的那么“昂贵”吗?) - Jon Skeet
是的,我希望泛型能给我正确的类型。我在我的脚本编译器中使用它。基本上,我只需要完整的运行时类型名称来检查脚本是否已经被编译和存在。GetType()提供了比名称更多的信息:-)。 - Kees C. Bakker

3

通常情况下,泛型是在编译时确定的,但它们也是运行时的特性。在这种情况下,您正在使用泛型类型推断,它使用变量等来推断类型。

在该方法中:

    public void Kick()
    {
        Printer.Print(this, Sound);
    } 

在这个上下文中,我们所知道的是this必须是一个Animal,因此存在一个隐含的动物,即Printer.Print<Animal>(this, Sound)

其他选项:

  • 使用GetType()来查找对象的实际类型
  • 使用dynamic将解析推迟到运行时(注意:这不是理想的dynamic使用方式,但它可以工作)

泛型是运行时特性吗? - Kees C. Bakker
1
@Kees 在 .Net 中,它们是;它们非常不像 c++ 模板。事实上,你可以在运行时完全声明一个类型,然后在运行时创建一个 List<T>Activator.CreateInstance(typeof(List<>).MakeGenericType(someType)) 等等。这也就是为什么你可以使用来自库的泛型而无需源代码或头文件——它是运行时支持泛型的。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接