无法将泛型类型的对象强制转换为通用接口C#

时间:2017-07-07 15:46:40

标签: c# generics inheritance multiple-inheritance

我有两个界面:

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()) {}
}
  

规则B:规则A:规则&lt; DmModel,Middle,DbModel&gt; :IRule&lt; IDmModel,IDbModel&gt; :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

3 个答案:

答案 0 :(得分: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&lt; DmModel,DbMode&gt;

这不能转换为IRule&lt; IDmModel,IDbModel&gt;。 C#不支持这种类型的转换。出于同样的原因,您无法执行List<object> b = (List<object>)new List<string>();(Gives“无法将类型'System.Collections.Generic.List&lt; string&gt;转换为System.Collections.Generic.List&lt; object&gt;。”)

这是协方差问题。

以下是Microsoft提供的有关此主题的更多信息:https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

答案 1 :(得分:3)

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

也许这就是你的意思:

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

编译得很好,我用这种方式构建你的课程,这是除了重组之外的唯一选择。

答案 2 :(得分:3)

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

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