【问题标题】:Interface implementation confusion接口实现混乱
【发布时间】:2012-07-21 19:21:15
【问题描述】:

假设你有这个:

// General purpose
public interface ISerializer
{
    IDataResult Serialize<T>(T instance);
}

// General purpose
public interface IDataResult
{
}

// Specific - and I implement IDataResult
public interface IMyCrazyDataResult : IDataResult
{
}

public class MyCrazySerializer : ISerializer
{
    // COMPILE ERROR:
    // error CS0738: 'MyCrazySerializer' does not implement interface member 'ISerializer.Serialize<T>(T)'. 
    // 'MyCrazySerializer.Serialize<T>(T)' cannot implement 'ISerializer.Serialize<T>(T)' because it does 
    // not have the matching return type of 'IDataResult'.
    public IMyCrazyDataResult Serialize<T>(T instance)
    {
        throw new NotImplementedException();
    }
}

到底为什么我会得到这个编译错误?我尊重接口——事实上,我确实返回了一个 IDataResult,尽管是间接的。是编译器无法解决这个问题,还是从根本上(在 OO 级别)有什么问题?

我认为拥有一个接口的全部意义在于我可以保证一些实现,但让我可以添加它。这就是我正在做的 - 但我得到一个编译错误。

在我的真实代码中,我希望返回类型更具体一点,因为我在派生接口中有几个附加方法。如果我将 MyCrazySerializer.Serialize 的返回类型设为 IDataResult 类型,那么智能感知只会向我显示基本的常用方法,我想在其中显示更具体的接口。

我还能如何做到这一点?这段代码有什么问题???

【问题讨论】:

标签: c# oop interface


【解决方案1】:

C# 不支持return type covariance,因此您需要完全按照界面上显示的方式实现Serialize&lt;T&gt; 方法。但是,您可以显式实现它,这意味着任何知道MyCrazySerializer 真实类型的客户端都可以访问更具体的方法:

public class MyCrazySerializer : ISerializer
{
    public IMyCrazyDataResult Serialize<T>(T instance)
    {
        throw new NotImplementedException();
    }

    IDataResult ISerializer.Serialize<T>(T instance)
    {
        return this.Serialize(instance);
    }
}

正如评论指出的那样,您可以简单地在显式实现中调用更具体的版本。

你可以用作:

IMyCrazyDataResult result = new MyCrazySerializer().Serialize<int>(1);
ISerializer serializer = (ISerializer)new MyCrazySerializer();
IDataResult = serializer.Serialize<int>(1);

【讨论】:

  • 我没有想到这个!这确实最接近地给了我想要的东西。我做 ISerialier s = new MyCrazySerializer().Seri... intellisense 首先显示我的自定义界面,但是通过转换,我也可以看到 IDataResult... 谢谢!
  • 如果您编写显式版本来调用另一个版本,您会得到我的 +1:IDataResult ISerializer.Serialize&lt;T&gt;(T instance { return Serialize(instance); } 如果您不能修改 ISerializer,这当然是这样做的方法界面。
【解决方案2】:

您可以在 C# 中构建自己的返回类型协方差:

// General purpose
public interface ISerializer<out TResult> where TResult : IDataResult
{
    TResult Serialize<T>(T instance);
}

// General purpose
public interface IDataResult
{
}

// Specific - and I implement IDataResult
public interface IMyCrazyDataResult : IDataResult
{
}

public class MyCrazySerializer : ISerializer<IMyCrazyDataResult>
{
    public IMyCrazyDataResult Serialize<T>(T instance)
    {
        throw new NotImplementedException();
    }
}

Serialize 的返回类型明确声明为派生自 IDataResult,而不是精确的 TResult

【讨论】:

  • 看来我不应该这样做 - 但这会起作用。理想情况下,我想让我的界面更干净一点。
【解决方案3】:

到底为什么我会得到这个编译错误?我尊重界面

不,你不是。您返回的不是IDataResult,而是IMyCrazyDataResult。是的,它继承自 IDataResult,但它与它相同。

当涉及到接口时,您不会得到差异 - 类型必须完全匹配。

【讨论】:

  • 为什么?拥有确切类型与派生类型的重要性是什么 - 这似乎是一个脆弱的想法。
  • @RobertSeder - 这就是语言设计者决定设计语言的方式。
【解决方案4】:

你没有履行接口契约,因为你指定了一些不同的东西作为方法的返回类型,签名必须完全匹配。这并不意味着您不能返回更指定的接口,以下是完全可以的:

// General purpose
public interface ISerializer
{
    IDataResult Serialize<T>(T instance);
}

// General purpose
public interface IDataResult
{
}

// Specific - and I implement IDataResult
public interface IMyCrazyDataResult : IDataResult
{
}

public class MyCrazySerializer : ISerializer
{
    public IDataResult Serialize<T>(T instance)
    {
        // return a IMyCrazyDataResult here
    }
}

【讨论】:

  • 我认为 OP 已经展示了足够的能力来说明他为什么希望它起作用
  • 为什么它需要完全匹配,而不尊重派生类型?为什么会有如此脆弱的实现?为什么不允许 OO 原则?为什么不允许派生类型 - 这是 OO 的核心!!?!?
  • 请参阅 Eric Lippert's answer(C# 编译器团队的开发人员之一)了解该主题。
【解决方案5】:

与 C++ 不同,C# 没有 covariant return type

【讨论】:

    猜你喜欢
    • 2011-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 2014-02-18
    • 1970-01-01
    相关资源
    最近更新 更多