【问题标题】:Unable to cast object of generic type to generic interface C#无法将泛型类型的对象转换为泛型接口 C#
【发布时间】:2017-07-07 15:46:40
【问题描述】:

我有两个接口:

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 : Rule : IRule : IRule

当我尝试将 RuleB 的对象转换为 IRule&lt;IDmModel, IDbModel&gt; 时发生未处理的异常

无法将“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

【问题讨论】:

    标签: c# generics inheritance multiple-inheritance


    【解决方案1】:

    你已经达到了很多间接级别......

    问题来了:

    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

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

    这是协方差的问题。

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

    【讨论】:

    • @kogoia 我做了更多的挖掘,并注意到您将接口参数分别声明为inout。这意味着您实际上可以这样做:var ruleB = (IRule&lt;DmModel, IDbModel&gt;)new RuleB(); DmModel 在这种情况下必须是具体的,但 IDbModel 可以作为接口留在演员表中。这对你想做的事情有帮助吗?
    • 不完全是,我想得到如上这样的结果(IRule&lt;IDmModel, IDbModel&gt;)new RuleB()
    • public RuleB() : base((dm) =&gt; new Middle()) {} 我希望 dm 在这里是 DmModel (dm) =&gt; new Middle(dm.SomeProperty)SomeProperti 是 DmModel 类的属性,它不在接口声明中,不能是
    • 好吧,你可以尝试将两个接口参数声明为out。我不明白你做得很好,不知道这是否可行。
    【解决方案2】:

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

    也许这就是你的意思:

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

    编译得很好,而且我以这种方式构建你的类,是除重组之外的唯一选择。

    【讨论】:

    • 是的,它可以编译并且可以工作,但是我在进行强制转换时不知道此类的类型。我的意思是DmModelDbModel
    • OP 的问题不是编译,而是运行时异常。不过,这个建议似乎确实可以解决问题。
    【解决方案3】:

    你的接口IRule&lt;in TInput, out TOutput&gt; 是协变的 逆变的,这意味着你不能协变地转换它。逆变可以防止这种情况发生。

    基本上,您的赋值 var dbModel = (IRule&lt;IDmModel, IDbModel&gt;)new RuleB(); 断言 dbModel 必须接受 any IDmModel 参数。不幸的是,这不是真的。由于RuleA 的具体形式,实例必须可分配给DmModel,因此IDmModel 的其他派生将失败。

    【讨论】:

    • 这很好地解释了为什么不允许这种类型的转换。
    猜你喜欢
    • 2013-01-08
    • 2016-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-11
    • 2013-05-23
    • 1970-01-01
    相关资源
    最近更新 更多