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

8

我有两个接口:

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< DmModel,Middle,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());

这个链接有什么问题?

为了让示例更加清晰,我对其进行了简化:

编辑:

在得到答案后,我理解了问题,并对示例进行了简化:

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个回答

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<string> 转换为类型 System.Collections.Generic.List<object>” 的错误)。

这是一个协变性问题。

下面是 Microsoft 对此主题的更多信息:https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance


1
@kogoia 我进行了更深入的挖掘,并注意到您分别将接口参数声明为 inout。这意味着您实际上可以这样做:var ruleB = (IRule<DmModel, IDbModel>)new RuleB(); 在这种情况下,DmModel 必须是具体的,但是在转换中 IDbModel 可以保留为接口。这对您正在尝试做的事情有所帮助吗? - Chris Berger
不完全是,我想要像上面的那样得到这样的结果 (IRule<IDmModel, IDbModel>)new RuleB() - giokoguashvili
public RuleB() : base((dm) => new Middle(dm.SomeProperty)) {} 我希望这里的 dmDmModel,其中 SomePropertyDmModel 类中的属性,该属性未在接口声明中,并且不能被访问。 - giokoguashvili
好的,你可以尝试将两个接口参数都声明为 out。我不太了解你正在做什么,无法确定它是否有效。 - Chris Berger

4

这是一个相当令人困惑的例子,但我认为问题出在您将接口转换为泛型类型时,派生类强制您使用 DmModel 和 DbMode。

也许这才是你想要表达的:

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

这段代码编译没有问题,但如果按照你的类结构安排方式,除了重构之外,这似乎是唯一的选择。


是的,它可以编译和运行,但是当我进行强制类型转换时,我不知道这些类的类型。我的意思是 DmModelDbModel - giokoguashvili
1
OP的问题不是编译,而是运行时异常。然而,这个建议似乎解决了这个问题。 - CoderDennis

3

您的接口IRule<in TInput, out TOutput>既具有协变性具有逆变性,这意味着您无法进行协变转换。逆变性可以防止这种情况发生。

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


这是一个很好的解释,为什么不允许这种类型的转换。 - Chris Berger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接