c# .net 4.0 协变与逆变
c# .net 4.0 Covariant vs Contravariant
在使用逆变时,我试图让一些东西工作并与下面的问题作斗争。我的理解是协方差是你可以从基类型 return 派生类型的地方。逆变是您可以将基类型的派生类型作为参数传递给 class.
所以我有以下接口(逆变):
public interface IBase<in T> where T: BaseModel
{
void Process(T model);
}
然后我有一个摘要class
public abstract class Base<T>: IBase<T> where T: BaseModel
{
public virtual void Process(T model)
{
// throw new System.NotImplementedException();
}
}
还有一个具体的class
public class Parent: Base<ParentModel>
{
public override void Process(ParentModel model)
{
// throw new System.NotImplementedException();
}
}
考虑到通用类型仅用作输入而不是 return 类型,我不明白为什么我不能执行以下操作:
IBase<BaseModel> baseContravariant = new Parent();
// This doesn't compile. I will eventually have a list of IBase<BaseMode> to which I'd like to pass in different parent instances.
下面还有另一个使用协方差的示例,效果很好。
public interface IBase<out T> where T : BaseModel, new()
{
T ProcessAndGet();
}
摘要
public abstract class Base<T>: IBase<T> where T: BaseModel, new()
{
public virtual T ProcessAndGet()
{
var result = new T() as BaseModel;
// More shizzle here
return (T)result;
}
}
混凝土
public class Parent : Base<ParentModel>
{
public override ParentModel ProcessAndGet()
{
var x = base.ProcessAndGet();
return x;
}
}
现在我可以做
IBase<BaseModel> baseInstance = new Base<BaseModel>();
IBase<BaseModel> derived = new Parent();
baseInstance = derived;
上面的示例还有更多代码,但为了便于阅读,我已将其删除(希望如此!):-)
这种情况下的逆变意味着您需要传入指定类型或 "more specialised"(=> 从基类型派生)的类型。
由于第一个示例中的 Parent
实现只能处理 ParentModel
,因此传入 BaseModel
实例是无效的。尝试做 new Parent().Process(new BaseModel())
也不会编译。所以强制转换为IBase<BaseModel>
是无效的。 (假设 ParentModel
是 BaseModel
的子 class)。
在这种情况下,通过认为 IBase<in T>
"consumes a T" 更容易推理出逆变模型。所以 IBase<ParentModel>
"consumes ParentModel
s"。这意味着它只能传递作为 ParentModel
实例的值,或者可以被视为一个值(实际上只有 subclasses)。
在您的第二个示例中,您使用的是 <out T>
,即 "covariant"。这可以描述为"it produces instances of T
"。因此 class "produces" 和 ParentModel
自动也是 BaseModel
的 "producer":因为 ParentModel
可以大小写为 BaseModel
, IBase<ParentModel>
也可以转换为 IBase<BaseModel>
。
在使用逆变时,我试图让一些东西工作并与下面的问题作斗争。我的理解是协方差是你可以从基类型 return 派生类型的地方。逆变是您可以将基类型的派生类型作为参数传递给 class.
所以我有以下接口(逆变):
public interface IBase<in T> where T: BaseModel
{
void Process(T model);
}
然后我有一个摘要class
public abstract class Base<T>: IBase<T> where T: BaseModel
{
public virtual void Process(T model)
{
// throw new System.NotImplementedException();
}
}
还有一个具体的class
public class Parent: Base<ParentModel>
{
public override void Process(ParentModel model)
{
// throw new System.NotImplementedException();
}
}
考虑到通用类型仅用作输入而不是 return 类型,我不明白为什么我不能执行以下操作:
IBase<BaseModel> baseContravariant = new Parent();
// This doesn't compile. I will eventually have a list of IBase<BaseMode> to which I'd like to pass in different parent instances.
下面还有另一个使用协方差的示例,效果很好。
public interface IBase<out T> where T : BaseModel, new()
{
T ProcessAndGet();
}
摘要
public abstract class Base<T>: IBase<T> where T: BaseModel, new()
{
public virtual T ProcessAndGet()
{
var result = new T() as BaseModel;
// More shizzle here
return (T)result;
}
}
混凝土
public class Parent : Base<ParentModel>
{
public override ParentModel ProcessAndGet()
{
var x = base.ProcessAndGet();
return x;
}
}
现在我可以做
IBase<BaseModel> baseInstance = new Base<BaseModel>();
IBase<BaseModel> derived = new Parent();
baseInstance = derived;
上面的示例还有更多代码,但为了便于阅读,我已将其删除(希望如此!):-)
这种情况下的逆变意味着您需要传入指定类型或 "more specialised"(=> 从基类型派生)的类型。
由于第一个示例中的 Parent
实现只能处理 ParentModel
,因此传入 BaseModel
实例是无效的。尝试做 new Parent().Process(new BaseModel())
也不会编译。所以强制转换为IBase<BaseModel>
是无效的。 (假设 ParentModel
是 BaseModel
的子 class)。
在这种情况下,通过认为 IBase<in T>
"consumes a T" 更容易推理出逆变模型。所以 IBase<ParentModel>
"consumes ParentModel
s"。这意味着它只能传递作为 ParentModel
实例的值,或者可以被视为一个值(实际上只有 subclasses)。
在您的第二个示例中,您使用的是 <out T>
,即 "covariant"。这可以描述为"it produces instances of T
"。因此 class "produces" 和 ParentModel
自动也是 BaseModel
的 "producer":因为 ParentModel
可以大小写为 BaseModel
, IBase<ParentModel>
也可以转换为 IBase<BaseModel>
。