【问题标题】:error when returning generic interface返回泛型接口时出错
【发布时间】:2013-04-03 14:06:39
【问题描述】:

以下代码是我正在编写的程序的一个简单示例。

public class Y
{ }

public class X : Y
{ }

public class W : Y
{ }

public interface IAaa<T>
    where T : Y
{
    void Execute(T ppp);
}

public abstract class Aaa<T> : IAaa<T>
    where T : Y
{
    public abstract void Execute(T ppp);
}

public class Bbb : Aaa<X>
{
    public override void Execute(X ppp)
    { }
}

public class Ccc : Aaa<W>
{
    public override void Execute(W ppp)
    { }
}

public class Factory 
{
    public static IAaa<Y> Get(bool b)
    {
        if(b)
            return new Bbb();
        else
            return new Ccc();
    }
}

class Program
{
    static void Main(string[] args)
    {
        IAaa<Y> aa;
        aa = Factory.Get(true);
    }
}

当我编译它时,我得到以下错误

错误 CS0266:无法隐式转换类型“ConsoleApplication3.Bbb” 到“ConsoleApplication3.IAaa”。显式 存在转换(您是否缺少演员表?)

错误 CS0266:无法隐式转换类型“ConsoleApplication3.Ccc” 到“ConsoleApplication3.IAaa”。显式 存在转换(您是否缺少演员表?)

有没有办法让它工作?

【问题讨论】:

  • 比起你的问题,我的建议是使用更有意义的类名;)
  • 您遗漏了一些东西/过度简化了您的示例 - Get 方法在其定义和来自 Main() 的调用中都需要一个类型参数
  • 我相信简单地询问新的 Bbb 或 Ccc 不会告诉基类 Aaa 中的类型参数是什么。

标签: c# generics inheritance


【解决方案1】:

您无法以您尝试的方式使用该界面。查找covariance/contravariance,您正在尝试做与可能相反的事情(您在界面中可能是&lt;in T&gt;,但您尝试像&lt;out T&gt; 一样使用它)。

Bbb 类为例 - 它有一个 Execute(X) 方法。如果您尝试将Y(可能是也可能不是X)传递给它会发生什么?编译器不允许这样做,因为您从未在代码中定义在这种情况下会发生什么。

您可以通过创建和实现另一个接口IAaa 来做您想做的事。例如

public interface IAaa
{
    void Execute(Y ppp);
}

可能是这样实现的,这样如果你尝试用无效类型调用它,就会抛出一个强制转换异常:

void Main()
{
        IAaa aa;
        aa = Factory.Get(true);
}

public class Y
{ }

public class X : Y
{ }

public class W : Y
{ }

public interface IAaa<T> : IAaa
    where T : Y
{
    void Execute(T ppp);
}

public interface IAaa
{
    void Execute(Y ppp);
}

public abstract class Aaa<T> : IAaa<T>
    where T : Y
{
    public abstract void Execute(T ppp);
    void IAaa.Execute(Y ppp)
    {
        this.Execute(ppp);
    }
    protected abstract void Execute(Y ppp);
}

public class Bbb : Aaa<X>
{
    public override void Execute(X ppp)
    { }
    protected override void Execute(Y ppp)
    {
        this.Execute((X)ppp);
    }
}

public class Ccc : Aaa<W>
{
    public override void Execute(W ppp)
    { }
    protected override void Execute(Y ppp)
    {
        this.Execute((W)ppp);
    }
}

public class Factory 
{
    public static IAaa Get(bool b)
    {
        if(b)
            return new Bbb();
        else
            return new Ccc();
    }
}

【讨论】:

  • 谢谢。但我不明白您所说的“以 Bbb 类为例 - 它有一个 Execute(X) 方法。如果您尝试将 Y(可能是也可能不是 X)传递给它会发生什么?”。如果只能接受 X,我如何将 Y 传递给 B.Execute
  • 它有效,但对我来说似乎有点混乱。如果在您的 Main 方法中调用 Execute(new Y()) 它会失败,因为您正在转换。如果您调用 Execute(new X()) 它可以工作,但它实际上通过三种方法来执行您想要的操作:Aaa 类 Iaa.Execute() 和 Bbb 类 Execute()。如果那是您正在寻找的那种功能,它可能对您没问题。否则你可以尝试重构类和接口。
  • @amichaigerstl 您正在尝试将Bbb 的实例用作IAaa&lt;Y&gt;IAaa&lt;Y&gt; 定义了一个带有签名void Execute(Y) 的方法。您的Bbb 类定义了一个带有签名void Execute(X) 的方法。没有办法从Y 更改为X 没有失败的可能性。你说得对,Bbb.Execute 只能取X;你试图让它占用Y,这就是它无法编译的原因。
  • @AndreCruz 完全同意...这很令人困惑,但它是实现提问者尝试做的事情的一种方式:获取可能是 BbbCccIAaa&lt;Y&gt; 等价物。在查看此实现时,他可能会决定毕竟替代方案会更好。至于为什么我包含这么多方法:它使类的面向公众的方法类型安全,同时仍然为您提供转换接口,就像List&lt;T&gt; 及其IList&lt;T&gt;IList 接口:类型安全方法是面向公众的,但是对于接受 object 并在其中进行转换的方法存在接口。
【解决方案2】:

正如错误所说,您缺少演员表。我相信这就是您所需要的:

public static IAaa<Y> Get(bool b)
{
    if(b)
        return (IAaa<Y>)(new Bbb());
    else
        return (IAaa<Y>)(new Ccc());
}

【讨论】:

  • 感谢它编译知道但在运行时失败。有一个 InvalidCasting 异常
  • 您的工厂可能需要位于模板专业化的上游才能完成这项工作。
【解决方案3】:

您可以转换为 (IAaa&lt;Y&gt;) 并且您的代码将编译。 但是它不会工作并且会在运行时失败。 为什么? 您的类BbbCcc 是专门的类,Execute 方法不能处理Aaa 的所有类型。你必须告诉 C# / 编译器。

更新:

通过拥有通用工厂,您可以获得 IAaa 的专用实例,并且您的代码应该可以工作。 在您的Program 中,当您将 TRUE 或 FALSE 传递给工厂时,您已经知道类型,因此您需要明确告诉 C# 您要使用的接口实现的类型。 (相应地重构 Factory 类,我只是发送应该编译的内容)

public class Y
{ }

public class X : Y
{ }

public class W : Y
{ }

public interface IAaa<T>
    where T : Y
{
    void Execute(T ppp);
}

public abstract class Aaa<T> : IAaa<T>
    where T : Y
{
    public abstract void Execute(T ppp);
}

public class Bbb : Aaa<X>
{
    public override void Execute(X ppp)
    { }
}

public class Ccc : Aaa<W>
{
    public override void Execute(W ppp)
    { }
}

public class Factory<T> where T : Y
{
    public static IAaa<T> Get(bool b)
    {
        if(b)
            return (IAaa<T>)new Bbb();
        else
            return (IAaa<T>)new Ccc();
    }
}

class Program
{
    static void Main(string[] args)
    {
        IAaa<X> aa;
        aa = Factory<X>.Get(true);
    }
}

更新 2

只是一个如何重构 Factory 类的示例:

public class Factory<T, U>
    where T : Y
    where U : Aaa<T>, new()
{
    public static IAaa<T> Get()
    {
        return (IAaa<T>)new U();
    }
}

class Program
{
    static void Main(string[] args)
    {
        IAaa<X> aa;
        aa = Factory<X, Bbb>.Get();
    }
}

【讨论】:

  • 我尝试了投射,但出现了 InvalidCasting 异常。所以我什至没有进入执行部分。
猜你喜欢
  • 2015-05-20
  • 1970-01-01
  • 1970-01-01
  • 2016-10-16
  • 1970-01-01
  • 2013-06-28
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
相关资源
最近更新 更多