【问题标题】:Cannot cast concrete implementation to constrained type interface of implementation无法将具体实现转换为实现的约束类型接口
【发布时间】:2013-07-05 18:54:29
【问题描述】:

具有受限界面的复杂访客场景:

public enum EDTypes { A1, A2 }
public interface IProcing { string doIt(string value); }
public interface IFooIni : IProcing { }
public interface IFooEnd : IProcing { }
public class FooIni_A1 : IFooIni { public string doIt(string value) { return "itsIni_A01"; } }
public class FooEnd_A1 : IFooEnd { public string doIt(string value) { return "itsEnd_A01"; } }
public class FooIni_A2 : IFooIni { public string doIt(string value) { return "itsIni_A02"; } }
public class FooEnd_A2 : IFooEnd { public string doIt(string value) { return "itsEnd_A02"; } }
public interface IFooSet<H, T> : IProcing
    where H : IFooIni
    where T : IFooEnd
{
    H FooIni { get; set; }
    List<IProcing> FooBar { get; set; }
    T FooEnd { get; set; }
}

以及多个具体实现:

public class FooSet_A1 : IFooSet<FooIni_A1, FooEnd_A1>
{
    public FooIni_A1 FooIni { get; set; }
    public List<IProcing> FooBar { get; set; }
    public FooEnd_A1 FooEnd { get; set; }
    public string doIt(string value) { return "itsIni_FooSetA1"; }
}
public class FooSet_A2 : IFooSet<FooIni_A2, FooEnd_A2>
{
    public FooIni_A2 FooIni { get; set; }
    public List<IProcing> FooBar { get; set; }
    public FooEnd_A2 FooEnd { get; set; }
    public string doIt(string value) { return "itsIni_FooSetA2"; }
}

为什么做不到:

public class testfoo
{
    private IFooSet<IFooIni, IFooEnd> getInstance(EDTypes type)
    {
        IFooSet<IFooIni, IFooEnd> res = null;
        switch (type)
        {
            case EDTypes.A1:
                /*
                    Unable to cast object of type 
                    '_protoTest.FooSet_A1' 
                    to type 
                    '_protoTest.IFooSet`2[_protoTest.IFooIni,_protoTest.IFooEnd]'.
                */
                res = (IFooSet<IFooIni, IFooEnd>)new FooSet_A1();
                break;
            case EDTypes.A2:
                res = (IFooSet<IFooIni, IFooEnd>)new FooSet_A2();
                break;
        }
        return res;
    }
    public void testIt()
    {
        // +*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
        IFooSet<IFooIni, IFooEnd> A1 = (IFooSet<IFooIni, IFooEnd>)getInstance(EDTypes.A1);
        string x = A1.doIt("ASDFG");
        // +*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
    }
}

有人here 告诉要在上面创建另一个接口,但我的工厂结果需要有问题的接口结构。


@Eldritch Conundrum 告诉我“T 上的协方差”并帮助我 很多了解正在发生的事情。
我还没有解决这个问题的办法,并且对截止日期发疯了
但我的问题是“为什么”会发生,他解释得很好。
放更多细节。标记为答案。

非常感谢

在@Eldritch Conundrum 的最后评论之后,我使用了动态关键字并解决了我的问题。
我失去了所有的智能,但现在它的工作! 谢谢@Eldritch 难题
有代码:

*public enum EDTypes { A1, A2 }
public interface IProcing { string doIt(string value); }
public interface IFooIni : IProcing { }
public interface IFooEnd : IProcing { }
public class FooIni_A1 : IFooIni { public string doIt(string value) { return "itsIni_A01"; } }
public class FooEnd_A1 : IFooEnd { public string doIt(string value) { return "itsEnd_A01"; } }
public class FooIni_A2 : IFooIni { public string doIt(string value) { return "itsIni_A02"; } }
public class FooEnd_A2 : IFooEnd { public string doIt(string value) { return "itsEnd_A02"; } }
public interface IFooSet<H, T> : IProcing
    where H : IFooIni
    where T : IFooEnd
{
    H FooIni { get; set; }
    List<IProcing> FooBar { get; set; }
    T FooEnd { get; set; }
}
public class FooSet_A1 : IFooSet<FooIni_A1, FooEnd_A1>
{
    public FooIni_A1 FooIni { get; set; }
    public List<IProcing> FooBar { get; set; }
    public FooEnd_A1 FooEnd { get; set; }
    public string doIt(string value) { return "itsIni_FooSetA1"; }
}
public class FooSet_A2 : IFooSet<FooIni_A2, FooEnd_A2>
{
    public FooIni_A2 FooIni { get; set; }
    public List<IProcing> FooBar { get; set; }
    public FooEnd_A2 FooEnd { get; set; }
    public string doIt(string value) { return "itsIni_FooSetA2"; }
}
public class testfoo
{
    private IProcing getInstance(EDTypes type)
    {
        dynamic res = null;
        switch (type)
        {
            case EDTypes.A1:
                res = new FooSet_A1();
                break;
            case EDTypes.A2:
                res = new FooSet_A2();
                break;
        }
        return res;
    }
    public void testIt()
    {
        // +*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
        dynamic A1 = getInstance(EDTypes.A1);
        string s1 = A1.doIt("ASDFG");
        dynamic A2 = getInstance(EDTypes.A2);
        string s2 = A2.doIt("ASDFG");
        // +*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
    }
}*

【问题讨论】:

    标签: c#-4.0 design-patterns generics casting type-constraints


    【解决方案1】:

    因此,您希望将 IConstrained&lt;CtrA1, CtrB1, CtrC1&gt; 转换为 IConstrained&lt;IClassA, IClassB, IClassC&gt;,因为 CtrA1 实现了 IClassA(其他类似)。

    这有点像将IEnumerable&lt;string&gt; 转换为IEnumerable&lt;object&gt;

    .Net 允许它,因为 IEnumerable&lt;T&gt; 实际上被声明为 IEnumerable&lt;out T&gt;。这在 T 上称为协方差

    但是,如果您的 IConstrained&lt;&gt; 具有采用 IClassA 的方法,则这对您不起作用,因为那将是不安全的。你知道为什么吗?

    IEnumerable&lt;string&gt; 转换为IEnumerable&lt;object&gt; 是安全的,因为只能从IEnumerable 读取字符串/对象。 但是对于 List&lt;T&gt;,它不起作用,因为将 List&lt;string&gt; 转换为 List&lt;object&gt; 将使您能够将对象添加到列表中(字符串!)。

    【讨论】:

    • 非常感谢您的关注和帮助。现在有更多细节,我尝试了“out”关键字,但它不适合我的情况,我需要使用里面的类型。
    • 我可能错了,不能用我对这个话题的小看法来质疑或做任何事情,但恕我直言,一个语言制造商不允许我将一组猫放在动物类型的变量上,因为我害怕我将尝试在猫集上插入一条鱼,包装为一组动物听起来对我来说很疯狂,我的责任是不要把鱼放在集上,这是最小的预期,比如不要除以零或不要在空实例类上调用方法。协方差束缚了我的双手并剥夺了我有用的可能性,它就像程序员的绞刑架。
    • 有什么方法可以禁用它?
    • 既然您已经修改了这个问题,我不知道我是否真的理解您的问题。协变在这里不是你的敌人,它只是 C# 静态类型的健全性。您不能全局禁用静态类型,如果您希望这样做,您应该使用动态类型语言,例如 ruby​​、python 或 javascript。或者,也许您可​​以使用“动态”关键字,但我不确定它是否适合您的情况。
    • 您的动态关键字适合这个案例,对我帮助很大!我失去了智能感知,但没问题,很高兴它现在可以工作。现在对我的截止日期不那么疯狂了。我在上面的问题文本中添加了新代码。
    猜你喜欢
    • 2016-11-16
    • 1970-01-01
    • 1970-01-01
    • 2017-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    • 2021-12-25
    相关资源
    最近更新 更多