我看到"cannot convert expression to type while using a generic method"做错了什么?



我有一个List<GKComponentSystem<GKComponent>>和一个通用的Add<T>()方法,其中包含TGKComponent的约束。

为什么我无法将GKComponentSystem<T>实例添加到我的列表中?请参阅下面截取的代码:

List<GKComponentSystem<GKComponent>> _systems = new List<GKComponentSystem<GKComponent>>();
public void AddSystem<T>(int position = -1) where T : GKComponent
{
var system = new GKComponentSystem<T>();
_systems.Add(system);
}

错误:

参数 #1 无法转换 GameplayKit.GKComponentSystem表达式 键入 GameplayKit.GKComponentSystem

那是在_systems.Add(system)行中.

我以为我知道 C#,但这是我很高兴拥有 StackOverflow 的情况之一 - 我到底不明白这里是什么?

systemGKComponentSystem<T>T必须是GKComponent,所以system是一个GKComponentSystem<GKComponent>,我应该能够将其添加到我的列表中。

这是GKComponentSystem

public class GKComponentSystem<T> : NSObject where T : GKComponent

它的T也是一个GKComponent...

这是关于逆变的吗(我绝对必须了解更多的主题)?

下面是一个更简单的示例:

class Parent
{
}
class Child : Parent
{
}

class GenericClass<T> 
{
}

Parent p;
p = new Child();  // A child inherits from Parent, so this is allowed.
GenericClass<Parent> gp;
gp = new GenericClass<Child>();  // Not allowed! GenericClass<Child> does not inherit from GenericClass<Parent>

在您的示例中,T继承自GKComponent的事实不会转换为GKComponentSystem<T>可以转换为GKComponentSystem<GKComponent>的规则。

因此,现在让我们将其应用于列表。

List<Parent> l = new List<Parent>();
l.Add(new Child());  // A child can be converted to a Parent, this is OK

List<GenericClass<Parent>> gl = new List<GenericClass<Parent>>();
gl.Add(new GenericClass<Child>()); // A GenericClass<Child> does not convert to GenericClass<Parent>, so this is not allowed.

如果你真的希望它工作,你可以定义一个泛型接口。这些允许您使用out指定通用参数,如下所示:

interface IGenericClass<out T>
{
}
class GenericClass<T> : IGenericClass<T>
{
}
IGenericClass<Child> gcChild = new GenericClass<Child>();
IGenericClass<Parent> gcParent = gcChild; // This is allowed!
var l = new List<IGenericClass<Parent>>();
l.Add(new GenericClass<Child>()); // Also allowed

因此,将其应用于您的示例:

interface IGKComponentSystem<out T> 
{
}
class GKComponentSystem<T> : IGKComponentSystem
{
}
List<IGKComponentSystem<GKComponent>> _systems = new List<IGKComponentSystem<GKComponent>(); 
// Should work from there...  
public void AddSystem<T>(int position = -1) where T : GKComponent
{
var system = new GKComponentSystem<T>();
_systems.Add(system);
}

将自定义类型替换为 BCL 类型:

List<List<object>> _systems = new List<List<object>>();
public void AddSystem<T>(int position = -1) where T : class
{
var system = new List<T>();
_systems.Add(system);
}

现在,假设这是有效的,我要打电话给AddSystem<string>();.这将创建一个List<string>,并将其添加到_systems中。

现在,假设我打电话给_systems[0].Add(1);.没有什么可以阻止这一点。_systems是一个List<List<object>>,因此_systems[0]是一个List<object>,因此它的Add方法接受任何对象。

但我创造的是List<string>,而不是List<object>。应该无法向其添加int

编译器可以拒绝此操作的唯一方法是使List<string>List<object>的转换无效。或者在您的情况下,GKComponentSystem<GKComponent>转换GKComponentSystem<T>

这是关于逆变的吗(我绝对必须了解更多的主题)?

有点。接口和委托类型可以指定进行一些额外的保证。IEnumerable<string>toIEnumerable<object>转换是安全的,并且允许包含相应注释的类型。但是它不适用于类类型,并且可以在这样的界面中放置的内容有点限制,任何可能不安全的东西都是不允许的。

如果你想放在GKComponentSystem中的东西只允许读取,这意味着它没有List那样的问题,你可以创建一个IGKComponentSystem<T>接口,并存储一个List<IGKComponentSystem<GKComponent>>,并添加一个IGKComponentSystem<T>实例,没有问题。

如果你想放入GKComponentSystem的内容也允许写入(例如Add方法),那么转换本质上是不安全的,你需要重新考虑你的设计。

相关内容

最新更新