提问人:Nick Farsi 提问时间:7/22/2022 最后编辑:Nick Farsi 更新时间:8/5/2022 访问量:194
类型参数必须逆向有效
Type parameter must be contravariantly valid
问:
考虑一只鸟:
public interface IBird
{
}
public class Duck : IBird
{
public string DuckyProperty { get; set; } //this property is Duck specific!
}
还有一个鸟类处理器:
public interface IBirdProcessor<out T> where T : IBird
{
double GetBirdWeight(T obj); //this does not compile
}
public class DuckProcessor : IBirdProcessor<Duck>
{
public double GetBirdWeight(Duck duck)
{
return double.Parse(duck.DuckyProperty);
}
}
还有一家工厂得到一个鸟类处理器:
public class BirdProcessorFactory
{
public IBirdProcessor<T> GetBirdProcessor<T>(T obj) where T : IBird
{
return (IBirdProcessor<T>)new DuckProcessor();
}
}
启动:
static void Main()
{
var bird = new Duck() { DuckyProperty = "23" } as IBird; //the cast is necessary to simulate a real world scenario
var factory = new BirdProcessorFactory();
var provider = factory.GetBirdProcessor(bird);
var weight = provider.GetBirdWeight(bird);
}
我想要一个抽象的通用鸟工厂,但我收到以下错误:
The type parameter 'T' must be contravariantly valid on 'Program.IBirdProcessor<T>.GetBirdWeight(T)'. 'T' is covariant
如果我删除“out”关键字,那么我会得到:
System.InvalidCastException: 'Unable to cast object of type 'DuckProcessor' to type 'IBirdProcessor`1[NotVegetables.Program+IBird]'.'
演员表不起作用!
作为一种解决方法,我可以这样做:
public class DuckProcessor : IBirdProcessor<IBird>
{
public double GetBirdWeight(IBird bird)
{
var duck = bird as Duck;
return double.Parse(duck.DuckyProperty);
}
}
但这完全违背了泛型的使用,我觉得 DuckProcessor 应该与鸭子一起工作,而不是与抽象的鸟类一起工作。
我做错了什么吗?
答:
4赞
evan
7/30/2022
#1
C# 泛型中的所有类型都必须在编译时已知。只有在编译时才知道,并且工厂必须返回一个 .由于协方差不是一个选项(因为只接受 s),因此无法返回 a。IBird
IBirdProcessor<IBird>
DuckProcessor.GetBirdWeight
Duck
DuckProcessor
你可以得到你想要的行为:
public interface IBirdProcessor
{
double GetBirdWeight(IBird obj);
}
public abstract class BirdProcessorBase<T> : IBirdProcessor where T : IBird
{
public double GetBirdWeight(IBird bird) => GetBirdWeightInternal((T)bird);
protected abstract double GetBirdWeightInternal(T bird);
}
public class DuckProcessor : BirdProcessorBase<Duck>
{
protected override double GetBirdWeightInternal(Duck duck)
{
return double.Parse(duck.DuckyProperty);
}
}
public class BirdProcessorFactory
{
public IBirdProcessor GetBirdProcessor(IBird bird)
{
if (bird.GetType().IsAssignableTo(typeof(Duck)))
return new DuckProcessor();
throw new Exception($"No processor for {bird.GetType().Name}");
}
}
但是,如果您使用不匹配的类型调用从工厂返回的处理器,它将向您抛出异常。
0赞
Mykhailo Nohas
8/5/2022
#2
请看一下我对这个问题的解决方案:
public interface IBird
{
}
public class Duck : IBird
{
public string DuckyProperty { get; set; } //this property is Duck specific!
}
public class Chicken : IBird
{
public string ChickenProperty { get; set; } //this property is Chicken specific!
}
public interface IBirdProcessor<Y>
where Y : IBird
{
double GetBirdWeight(Y obj);
}
public class DuckProcessor : IBirdProcessor<Duck>
{
public double GetBirdWeight(Duck duck)
{
return double.Parse(duck.DuckyProperty);
}
}
public class ChickenProcessor : IBirdProcessor<Chicken>
{
public double GetBirdWeight(Chicken chicken)
{
return double.Parse(chicken.ChickenProperty);
}
}
public class BirdProcessorFactory
{
public static IBirdProcessor<T> GetProcessor<T>(T bird)
where T : IBird
{
switch (bird){
case Chicken t1:
return (IBirdProcessor<T>)new ChickenProcessor();
case Duck t1:
return (IBirdProcessor<T>)new DuckProcessor();
default:
throw new ArgumentException();
}
}
}
为了表明它按预期工作,我添加了额外的 .ChickenProcessor
执行如下所示:
Duck duck = new Duck() { DuckyProperty = "23" };
Chicken chicken = new Chicken() { ChickenProperty = "25" };
var duckProvider = BirdProcessorFactory.GetProcessor(duck);
var chickenProvider = BirdProcessorFactory.GetProcessor(chicken);
duckProvider.GetBirdWeight(duck).Dump();
chickenProvider.GetBirdWeight(chicken).Dump();
结果符合预期:
评论
0赞
Nick Farsi
8/5/2022
问题是,您在运行时知道类型,而工厂方法应该使用泛型(或抽象)类型并接受任何鸟。所以实际上你完全从问题中删除了工厂。
0赞
Mykhailo Nohas
8/5/2022
@NickFarsi 感谢您的解释,因此您基本上需要一种工厂方法,它将采用正确的处理器。我调整了代码。
0赞
Nick Farsi
8/6/2022
将 DuckProcessor 转换为 IBirdProcessor<IBird> 将导致运行时异常,这与我的原始示例非常相似
评论
Weight
IBird
((IBirdProcessor<IBird>)new DuckProcessor()).GetBirdWeight(new Sparrow()))
DuckProcessor
Sparrow
DuckProcessor
Sparow
DuckProcessor
IBirdProcessor<IBird>
IBird
IBirdProcessor<Duck>
GetBirdWeight
IBird
Weight{get;}