接口实现混淆

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
{
    // 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。是编译器无法理解还是在面向对象层面上有根本性的问题?
我认为拥有接口的全部意义在于我可以保证某些实现,但也让我有机会对其进行扩展。这就是我正在做的事情 - 然而我却得到了一个编译错误。
在我的真实代码中,我想让返回类型更加具体,因为我在派生接口中有几个额外的方法。如果我将 MyCrazySerializer.Serialize 的返回类型设为 IDataResult 类型,那么智能感知只会显示基本共同的方法,而我想显示一个更具体的接口。
我应该如何完成这个任务?这段代码有什么问题吗?

2
可能是Does C# support return type covariance?的重复问题。 - Euphoric
@euphoric - 我同意,在那篇其他的帖子上有很多好信息。 - Robert Seder
5个回答

5
C#不支持返回类型协变,因此您需要按照接口上显示的方式实现Serialize<T>方法。但是,您可以显式地实现它,这意味着任何知道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...谢谢! - Robert Seder
如果您编写显式版本来调用其他版本:IDataResult ISerializer.Serialize<T>(T instance { return Serialize(instance); },我会给您加1分。如果您无法修改ISerializer接口,则这肯定是解决问题的方法。 - Daniel Earwicker

5
你可以在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

这似乎不应该是我的工作 - 但这将会起作用。理想情况下,我希望我的接口更简洁。 - Robert Seder

3
您没有履行接口合约,因为您在方法的返回类型上指定了不同的内容,签名必须完全匹配。这并不意味着您不能返回更具体的接口,以下是完全正确的:
// 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
    }
}

我认为原帖作者已经展示了足够的能力,说明他为什么期望它能正常工作。 - parapura rajkumar
为什么需要完全匹配,而不是尊重派生类型?为什么实现如此脆弱?为什么不允许面向对象的原则?为什么不允许派生类型 - 这是面向对象的核心!!! - Robert Seder
请参考C#编译器团队中的开发人员之一Eric Lippert在该主题上的回答。 - Femaref

3
世上为什么我会出现这个编译错误?我遵守了接口。
不,你没有。你返回的不是 IDataResult,而是 IMyCrazyDataResult。是的,它继承自 IDataResult,但它并不完全相同。
在接口方面,你不会得到差异 - 类型必须完全匹配。

为什么要使用确切的类型而不是派生类型?这似乎是一个脆弱的想法,有何意义? - Robert Seder
@RobertSeder - 这就是语言设计者决定设计这种语言的方式。 - Oded

1

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