2017-06-29 70 views
0

好的。我会简短介绍一下。为什么这不起作用?为什么泛型方法不能用于继承和接口?

//Find<T> returns the T object with a given name 
//Not a true method, but that's simplier for the example. 
Warrior conan = Find<Warrior> ("Conan"); 
//Debug.Log is just printing the results 
Debug.Log (conan is Warrior); //True 
Debug.Log (conan is Character); //True, Warrior extends Character 
Character other = Find<Character> ("Conan"); // Null, Why ? 

我猜一般的c#方法在IL中是非常不同的,这就是为什么不起作用。但它很烦人。我做错了什么?有没有办法绕过这个?

编辑:

事实上,我的方法有点不同。我正在使用MVC,我想查找对应于模型的视图。

public ElementView<E> Find<E> (E model) { 

    ElementView<E>[] tab = gameBoardView.transform.GetComponentsInChildren<ElementView<E>>(); 
    foreach (ElementView<E> el in tab) 
     if (EqualityComparer<E>.Default.Equals(el.model, model)) 
      return el; 
    return null; 
} 

我用它是这样的:

ElementView<Warrior> w = Find<Warrior> (myCharacter); // good if my character is a warrior 
ElementView<Character> c = Find<Character> (myCharacter); // null 
+6

添加Find 的代码将有所帮助。 –

+4

请显示'Find'方法的来源。 – dasblinkenlight

+0

如何提出一个好问题:[问]以及如何提供良好的示例代码:[mcve] – AndyJ

回答

2

正如在评论中指出,你很可能自己回答这个问题,如果你想在泛型类型参数的学习差异。推荐阅读包括:

In C#, why can't a List object be stored in a List variable
C# variance problem: Assigning List as List
Contravariance explained
Difference between Covariance & Contra-variance
埃里克利珀的Wizards and warriors系列(此链接是第1部分)

也就是说,在努力解决您的直接关注,并可能提供一些实用的,可操作的信息......

你的方法打破了这里:

ElementView<E>[] tab = gameBoardView.transform.GetComponentsInChildren<ElementView<E>>(); 

具体来说,model的类型是Warrior,这将有由GetComponentsInChildren<T>()被搜索的ElementView<Warrior>集合中的一个实例。当您拨打Find<Character>()时,您可以调用通用方法GetComponentsInChildren<Character>(),该方法将搜索ElementView<Character>的实例。

由于ElementView<Warrior>不是ElementView<Character> —即,它不继承该类型—它被从搜索中排除。搜索将返回只有ElementView<Character>的实例,该实例不包括所讨论模型的视图。 (如果Character只是所有实际类型使用的基类,则返回的集合实际上可能为空;只有在组件集合中存在实际的ElementView<Character>对象时,才会收到该方法返回的任何对象。)

您是否可以对此做任何事情取决于类型ElementView<T>以及您是否能够在代码中引入接口类型来表示ElementView<T>对象。也就是说,这将是合法的,当你问ElementView<Character>类型的对象只有GetComponentsInChildren<T>()返回ElementView<Warrior>类型的对象:

  1. 您可以更改代码返回接口类型,而不是ElementView<T>类型的集合(例如IElementView<T>)和
  2. 可以将接口的类​​型参数T声明为协变;即out关键字,表示接口的所有成员只有返回类型为T的对象,并且不接受它们作为参数。

如果这些事情是真实的,那么你可以申报相应的接口,确保ElementView<T>实现该接口,然后ElementView<Warrior>类型的对象可以强制转换为接口类型IElementView<Character>。由于接口类型参数是Character,并且接口实现只能返回类型为Character的对象,所以对象实际上返回Warrior类型的对象的事实就没有问题。 Warrior(假定)是Character,因此返回Warrior满足接口返回Character值的声明。

如果你能满足这些要求,则GetComponentsInChildren<T>()方法可以返回IElementView<T>[]类型的数组,这实际上可以包含您感兴趣的对象,实际上是键入ElementView<U>其中U继承T(即GetComponentsInChildren<Character>()将返回IElementView<Character>[],其中找到IElementView<Warrior>的实例是有效的)。

当然,您还需要更改使用这些类型的代码,包括GetComponentsInChildren<T>()(取决于其实现方式)。这不是一个简单的“抛开开关”类型的变化来支持泛型类型的变化。但假设您的类型与变体声明兼容,这可能是一个值得的努力。

我不能提供任何具体建议,说明如何进行这些更改,或者甚至是可能的,因为您的问题不包括好的Minimal, Complete, and Verifiable code example。如果您想进行这些更改,我建议您先研究差异,然后自行努力更改代码。如果之后仍然有问题,请发布一个新问题,确保包含一个很好的MCVE,清楚地表明您正在尝试做什么,并且准确解释您仍然遇到的问题。