为什么静态类不能实现接口?

111

可能重复:
为什么C#不允许静态方法实现接口?

在我的应用程序中,我想使用一个数据访问仓库(TestRepository, SqlRepository, FlatFileRepository等)。由于这样的仓库将在整个应用程序中使用,因此将其设置为静态类似乎是很明智的做法,这样我就可以采取以下方式:

SqlRepository.GetTheThingById(5);

我希望我的存储库是可互换的,因此想要它们实现一个通用的接口:IRepository。但是当我尝试这样做时,出现了以下错误:

静态类无法实现接口

为什么不能实现呢?你建议我如何更改设计?有可以使用的模式吗?

更新:
五年后:这个问题被访问了20k+次,我了解到存储库模式的缺点,学习了IoC,并意识到我的问题表述不太好。

我并不是真正地询问接口在C#规范中的含义,而是为什么它故意以这种特定的方式限制我。

实际上,答案是调用实例方法和类型方法的语法不同。但是这个问题已经关闭了。


1
我认为这是一个重复的问题,类似于https://dev59.com/ZHVC5IYBdhLWcg3wixw0。 - Michael Stum
1
如果你的帖子没有被关闭为重复,你可以把 "inherit" 改成 "implement" 吗?这个问题困扰着我... - grenade
1
@MichaelStum 这不是任何一个问题的重复。那些问题是关于静态方法实现由这些方法成员所属的类实现的接口指定的方法。而这个问题是关于静态实现接口的。 - DCShannon
我为链接的重复问题写了一个答案,但由于它实际上是这个问题的答案,所以对那个问题来说毫无意义。而且,由于这里被错误关闭,我也不能在这里发布它。我宁愿不要打开一个“真正”的重复问题。 - DCShannon
@BorisCallens,你能否推荐一些关于你提到的仓储模式缺点的好资料给我吗? - Marc.2377
显示剩余4条评论
3个回答

48

接口不能具有静态方法。实现接口的类需要将所有方法都作为实例方法实现。静态类无法拥有实例方法。

2022年更新: .NET 7 支持 静态接口方法


105
正确但并非“为什么”问题的答案。 - Quango
10
确实如此。每个句子都逻辑连贯,引出下一个句子。再次阅读他的回答,分析它,让它深入心底。;-) - Riegardt Steyn
40
@Heliac:如果观众不接受前提条件,那么这个论点就不具有说服力。如果Boris理解为什么这个前提条件是正确的,那他为什么不会理解静态类无法实现接口呢? - trolox
18
为什么接口不能拥有静态方法(或者说为什么接口的方法无法根据实现方式同时被视为静态和实例方法)?这其实回到了最初的问题。这种情况经常发生,当一个自证不明的答案被提供时。 - Juan
3
不回答为什么。假设你可以在静态类中实现一个接口,你会如何使用它?你不能传递一个静态类的引用,所以你不能有一堆方法或类具有接口而不是特定类型的签名。因此,你不能将该类用于依赖注入,这可能是你想要使用接口的确切方式。为什么C#不将静态类视为隐式单例类,以便你可以传递它,这是另一个问题,我无法回答。 - Mike
显示剩余3条评论

18

也许我们的经验可以帮忙。我们使用AutoFac来注入,而不是将SqlRepository作为静态类,同时将容器隐藏在一个静态类后面。然后每个实体都有一个静态的存储库属性:

public class Part : inheritence...
{
    public static IPartRepository Repository
    {
        get { return IoCContainer.GetInstance<IRepository<Part>>(); }
    }
    // ... more part-y stuff
}

这样我们就可以交换实现方式,调用者总是知道如何获取它:

Part p = Part.Repository.Get(id);

在另一个项目中,有一个已经在容器中注册的PartRepository:

public class PartRepository : IPartRepository
{
    // IPartRepository implementation that talks to injected DAL
}

在另一个项目中,我们使用模拟对象进行测试,包括预加载已知条目的存储库:

public class MockPartRepository : Dictionary<Part, int>, IPartRepository
{
    // IPartRepository implementation based on dictionary
}

...并且它已在容器中为单元测试注册。相同的调用获取存储库:

Part p = Part.Repository.Get(id);

你能详细说明一下吗?再提供一些代码吗? - Dennis G
1
@moontear:我们已经转换到了AutoFac,但是概念是相同的。请查看答案的编辑。 - n8wrl
1
哇,希望我能够点赞更多。3年后更新 :-) 我们正在使用Spring.Net,我考虑只使用单例(用于日志框架),而不是构建完整的存储库。否则,我很喜欢你的解决方案! - Dennis G

14

根据定义,接口创建了实例需要满足的契约。由于您不能实例化静态类,因此静态类无法实现接口。

没有必要拥有静态仓库。只需将其设置为非静态并在需要时实例化即可。


23
根据定义,接口会创建联系(例如可以被调用的方法)。静态类具有可以被调用的方法。因此,静态类可以履行合同。 - Ian Boyd
2
这对于通用接口定义是正确的,但这个问题是关于C#的,你的定义是不完整的。 - JoshJordan

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