为什么通用方法不能用于继承和接口?

时间:2017-06-29 16:07:46

标签: c# generics inheritance interface

好。我会简短一点。为什么这不起作用?

//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

1 个答案:

答案 0 :(得分: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
Eric Lippert的Wizards and warriors系列(此链接指向第1部分)

尽管如此,努力解决您的直接关注并可能提供一些实用,可操作的信息......

你的方法在这里打破:

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

具体来说,model的类型为WarriorElementView<Warrior>搜索的集合中的GetComponentsInChildren<T>()实例将被Find<Character>()搜索。当您致电GetComponentsInChildren<Character>()时,您会调用通用方法ElementView<Character>,该方法会搜索ElementView<Warrior>的实例。

由于ElementView<Character>不是ElementView<Character> - 也就是说,它不会继承该类型 - 它会从搜索中排除。搜索将返回{em>仅{/ 1}}的实例,这些实例将不包含相关模型的视图。 (如果Character只是所有真实类型使用的基类,则返回的集合实际上可能是空的...只有在组件集合中有实际的ElementView<Character>对象时,您才会得到任何对象通过该方法返回。)

您是否可以对此做任何事情取决于类型ElementView<T>以及您是否能够在代码中引入接口类型来表示ElementView<T>个对象。也就是说,GetComponentsInChildren<T>()仅在以下情况下请求类型ElementView<Warrior>的对象时才返回ElementView<Character>类型的对象是合法的:

  1. 您可以更改代码以返回接口类型的集合,而不是ElementView<T>类型(例如IElementView<T>)和
  2. 接口的类型参数T可以声明为协变;也就是说,使用out关键字,表示接口的所有成员只能返回类型为T的对象,而不接受它们作为参数。
  3. 如果这些都是真的,那么你可以声明适当的接口,确保ElementView<T>实现该接口,然后类型为ElementView<Warrior>的对象可以转换为接口类型{{1} }。由于接口类型参数为IElementView<Character>,并且接口实现只能返回类型为Character的对象,因此对象实际返回类型为Character的对象的事实可以。 Warrior(大概)是Warrior,因此返回Character会满足接口返回Warrior值的声明。

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

    当然,您还需要更改使用这些类型的代码,包括IElementView<Character>[](取决于其实现)。这不是一种简单的“抛弃切换”类型的变化来支持泛型类型的变化。但假设您的类型与变体声明兼容,那么这是值得的。

    我无法就如何进行这些更改提供任何具体建议,或者即使它们是可能的,因为您的问题不包括好Minimal, Complete, and Verifiable code example。如果您想进行这些更改,我建议您先研究差异,然后自行尝试更改代码。如果你之后仍然遇到麻烦,请发一个新问题,确保包含一个好的MCVE,清楚地显示你想要做的事情,并准确地解释你仍然遇到麻烦的事情。