2017-07-07 78 views
5

我有两个接口:无法转换泛型类型的对象,以通用接口C#

public interface IDbModel {} 
public interface IDmModel {} 

和类源于此:

public class DbModel : IDbModel {} 
public class DmModel : IDmModel {} 
public class Middle { } 

我也有两个接口的限制:

public interface IRule { } 
public interface IRule<in TInput, out TOutput> : IRule 
    where TInput : IDmModel 
    where TOutput : IDbModel 
{ 
    TOutput Apply(TInput elem); 
} 

还有一个从这个接口派生的抽象类:

public abstract class Rule<TDmModel, TMiddle, TDb> : IRule<TDmModel, TDb> 
    where TDmModel : IDmModel 
    where TDb : IDbModel 
{ 
    private readonly Func<TDmModel, TMiddle> _rule; 
    protected Rule(Func<TDmModel, TMiddle> rule) { _rule = rule; } 
    protected abstract TDb Apply(TMiddle transformedMessage); 
    public TDb Apply(TDmModel elem) { ... } 
} 

在此之后,我创建这个抽象类派生的两个类:

public class RuleA : Rule<DmModel, Middle, DbModel> 
{ 
    public RuleA(Func<DmModel, Middle> rule) : base(rule) {} 
    protected override DbMode Apply(Middle transformedMessage) { ... } 
} 

public class RuleB : RuleA 
{ 
    public RuleB() : base((dm) => new Middle()) {} 
} 

RuleB:RuleA:规则< DmModel,中东,DbModel之后>:的iRule < IDmModel,IDbModel>:iRule中

而且当我尝试将RuleB的对象投射到IRule<IDmModel, IDbModel>时,未处理的异常

无法投射'ParsreCombinators.RuleB'类型的对象来键入'ParsreCombinators.IRule`2 [ParsreCombinators.IDmModel,ParsreCombinators.IDbModel]'。

var ruleB = (IRule<IDmModel, IDbModel>)new RuleB(); // Exception 
IDbModel dbModel = ruleB.Apply(new DmModel()); 

什么错this

为了让这个例子较少混乱我把它简化:

编辑:

我明白的答案后,是什么问题,为了让这个例子不那么容易混淆,我简化了它:

public interface IDbModel {} 
public interface IDmModel {} 

public class DbModel : IDbModel {} 
public class DmModel : IDmModel {} 

public interface IRule<in TInput, out TOutput> 
    where TInput : IDmModel 
    where TOutput : IDbModel 
{ 
    TOutput Apply(TInput elem); 
} 

public class RuleA : IRule<DmModel, DbModel> 
{ 
    public DbModel Apply(DmModel elem) { ... } 
} 

var ruleA = (IRule<IDmModel, IDbModel>)new RuleA(); // Exception 

回答

7

这是很多你到了那里间接的水平......

这里的问题:

public abstract class Rule<TDmModel, TMiddle, TDb> : IRule<TDmModel, TDb> 
    where TDmModel : IDmModel 
    where TDb : IDbModel 

public class RuleA : Rule<DmModel, Middle, DbMode> 
public class RuleB : RuleA 
... 
var ruleB = (IRule<IDmModel, IDbModel>)new RuleB(); 

RuleB实现的iRule < DmModel,DbMode >

这不能转换到的iRule < IDmModel,IDbModel >。 C#不支持这种类型的转换。出于同样的原因,你不能这样做List<object> b = (List<object>)new List<string>();(给出“无法将类型“System.Collections.Generic.List <串>到System.Collections.Generic.List <对象>。”)

这是协方差问题。

下面是来自微软一些关于这个问题的更多信息:https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

+1

@kogoia我做了更多的挖掘,并且注意到你分别声明了你的接口参数为'in'和'out'。这意味着你实际上可以这样做:'var ruleB =(IRule )new RuleB();'DmModel在这种情况下必须是具体的,但是IDbModel可以作为接口保留在cast中。这是否有助于你想要做什么? –

+0

不是,我想得到如上所示的结果'(IRule )new RuleB()' – kogoia

+0

'public RuleB():base((dm)=> new Middle()){}'我想在这里'(dm)=>新中间(dm.SomeProperty)'中'dm'是'DmModel','SomeProperti'是DmModel类的属性,它不在接口声明中,不能是 – kogoia

3

这是一个非常令人困惑的例子,但我相信问题在于你正在转换为泛型类型并为其提供接口,当你派生的类迫使你使用DmModel和DbMode时。

也许这就是你的意思:

var ruleB = (IRule<DmModel, DbMode>)new RuleB(); 

,编译就好了,我用这种方式你的类是结构化的,比其他重组是唯一的选择。

+0

是的,它编译和工作,但我不知道当我做铸造时这个类的类型。我的意思是'DmModel'和'DbModel' – kogoia

+1

OP的问题不是编译,而是一个运行时异常。但是,这个建议似乎解决了这个问题。 – CoderDennis

3

你的界面IRule<in TInput, out TOutput>既是协逆变,这意味着你不能协变投它。逆变防止了这一点。

基本上,您的任务var dbModel = (IRule<IDmModel, IDbModel>)new RuleB();断言dbModel必须接受任何IDmModel参数。不幸的是,这不是事实;由于RuleA的具体形式,实例必须可分配到DmModel,因此IDmModel的其他衍生产品将失败。

+0

这是一个很好的解释,为什么这种类型的铸造是不允许的。 –

相关问题