强制转换不会改变对象的类型。对象始终保持与创建时相同。强制转换只是更改您访问对象的接口。如果您愿意,它会改变您对对象的“视图”。不过,这不是问题。你在做什么是正确的。UI 层关心的只是 ISurveyAnswer 接口,所以只要对象的具体类型实现了该接口,那么一切都很好,并且会按您的预期工作。这样做的美妙之处在于,现在 UI 层可以被赋予任何类型的对象(包括模拟对象),这无关紧要,只要对象实现了相同的接口,UI 就可以正常工作并且不会在意。GetType 将让您检查对象以查看对象的实际类型,但在大多数情况下,它不应该 对 UI 层无关紧要。只要提供的对象实现了该接口并正常工作,UI 也将如此。
当任何类或方法要求将某种类型的对象传递给它时,理想情况下,它应该始终要求尽可能窄的实现或基类型。因此,例如,如果您有一个获取项目列表的方法,它可能看起来像这样:
void printList(List<Dog> dogs)
{
foreach(Dog dog in dogs)
printDog(dog);
}
但是,从技术上讲,该方法实际上并不需要List<>
对象,因为它所做的只是枚举列表中的项目。它本身不使用类型的任何成员List<>
,它只是使用IEnumerable<>
由类型实现的接口的成员List<>
。因此,在这种情况下,最好像这样编写方法:
void printList(IEnumerable<Dog> dogs)
{
foreach(Dog dog in dogs)
printDog(dog);
}
现在,如果在方法内部,不仅需要遍历列表,还需要列表中项目的总数,那就IEnumerable
不够了。但是,它仍然不需要完整List<>
类型,它只需要ICollection<>
类型(也由 实现List<>
),如下所示:
void printList(ICollection<Dog> dogs)
{
printTotal(dogs.Count);
foreach(Dog dog in dogs)
printDog(dog);
}
但是,如果该方法需要获取每个项目的索引,ICollection<>
这还不够——该方法需要一个IList<>
类型。例如,此示例显示了它如何打印狗列表并突出显示列表中的所有其他狗:
void printList(IList<Dog> dogs)
{
printTotal(dogs.Count);
for(Dog dog in dogs)
if (dogs.IndexOf(dog) % 2 == 0)
printDog(dog);
else
printHighlightedDog(dog);
}
您会注意到,在这些示例中,我都不需要将dogs
参数转换为不同的类型。我根据需要适当地键入了参数,所以我只是通过该类型的接口使用它。在所有这些示例中,List<Dog>
可以将对象传递到printList
方法中而无需强制转换(因为 List<> 实现了所有这些接口,因此可以隐式强制转换):
List<Dog> dogs = new List<Dog>();
dogs.Add(new Dog());
printList(dogs);
请记住,这是一个非常简单的例子来说明一个观点。在现实世界的场景中,出于各种原因,您通常可能只是要求,List<>
或者IList<>
即使您在技术上并不需要他们提供的一切。您可能知道该方法将来很可能需要IList<>
功能,即使它现在只需要IEnumerable<>
功能。但是,当您决定方法参数的类型时,请牢记这一原则。
您几乎不需要知道传递给方法的对象的实际类型。您真正需要知道的是它实现了一个特定的接口,以便您可以通过该特定接口访问它的功能。