如何在接口上实现静态方法?


90

我有一个从C#调用的第三方C ++ DLL。

该方法是静态的。

我想将其抽象出来以进行一些单元测试,所以我创建了一个带有静态方法的接口,但是现在我的程序出现以下错误:

修饰语“静态”对此物品无效

MyMethod cannot be accessed with an instance reference; qualify it with a type name instead

如何实现这种抽象?

我的代码看起来像这样

private IInterfaceWithStaticMethods MyInterface;

public MyClass(IInterfaceWithStaticMethods myInterface)
{
  this.MyInterface = myInterface;
}

public void MyMethod()
{
  MyInterface.StaticMethod();
}

3
:也许你可以使用扩展方法做stackoverflow.com/questions/1243921/...
六氯苯

Answers:


47

您无法在C#中的接口上定义静态成员。接口是实例的合同。

我建议您像当前一样创建接口,但不要使用static关键字。然后创建一个StaticIInterface实现该接口并调用静态C ++方法的类。要进行单元测试,请创建另一个类FakeIInterface,该类也实现接口,但是可以执行处理单元测试所需的操作。

一旦定义了这两个类,就可以创建环境所需的一个,并将其传递给MyClass的构造函数。


63
-1的说法An interface is a contract, not an implementation.-是的,但是在这里完全无关的(non sequitur),因为静态方法不是实现本身的一部分-根据定义,实现是基于data的,而静态成员则无法访问dataAn interface type definition can define and implement static methods (see §8.4.3) since static methods are associated with the interface type itself rather than with any value of the type.-请记住,static成员通常是实用方法

2
我理解并同意您的声明,并且我认为您的评论也很重要。虽然。在设计接口时,应该将其视为合同,这意味着静态方法不适用。我认为我应该将其保留在那里,以帮助某些人了解界面的目的。社区是否认为应将其删除?
davisoa '16

1
我部分同意这An interface is a contract, not an implementation是无用的,有时上下文化确实有帮助。我完全同意static method is not a part of implementation itself ,静态方法一个实现,只有在其他方法的实现中用作静态实现时,它们才成为实现的一部分。但是,据我所知,我的词典是基于所学知识,术语的确也因编程语言而异。静态方法不能是接口,因为无论如何只能有一种实现。
CoffeDeveloper

想象一下,我有一个IPerson合同,其中规定了GetCountry该人的原籍国名称...FrenchPerson实体都将说“法国”,并且GermanPerson都将说“德国”,这在不同类型的实体共享相同的(数据)表时也很有用,例如MS Azure的一个,说ConnectionPostComment存储在UsersAzureTable,所以树的实体有一个共同的信息,IUsers可以有GetTableName静态的方法...
塞尔

@vaxquis-恕我直言,如果句子改写为“它是一个合同”将是有意义的:“接口是实例的合同。静态成员是该类型的一部分;这个改写的句子(正确)说它们在实例合同中没有意义。因此,我认为问题仅在于措词不准确,而不是胡说八道。
ToolmakerSteve

111

接口不能具有静态成员,并且静态方法不能用作接口方法的实现。

您可以做的是使用显式接口实现:

public interface IMyInterface
{
    void MyMethod();
}

public class MyClass : IMyInterface
{
    static void MyMethod()
    {
    }

    void IMyInterface.MyMethod()
    {
        MyClass.MyMethod();
    }
}

另外,您可以简单地使用非静态方法,即使它们不访问任何特定于实例的成员。


17
对于任何想知道为什么有人愿意这样做的人,当为实现静态方法的旧代码编写单元/集成测试时,它特别有用。
Dezzamondo

这项技术对于实现快速的RESTful API效果很好,该API需要持久化数据但不能使用数据库。该实现仅与内存中的C#对象一起使用,因此没有存储数据的地方,但是使用静态属性可以减轻对使用EF Core或SQLite的内存中数据库的需求。
gware

19

静态成员在CLR中是完全合法的,但不是C#。

您可以在IL中实现一些粘合,以链接实现细节。

不确定C#编译器是否允许调用它们?

请参阅:8.9.4接口类型定义ECMA-335。

接口类型必然是不完整的,因为它们对接口类型的值的表示什么也没有说。出于这个原因,尽管接口类型定义可以声明静态字段(见第8.4.3节),但它不应为接口类型的值(即实例字段)提供字段定义。

类似地,接口类型定义不得为其类型值提供任何方法的实现。但是,接口类型定义可以并且通常会定义方法约定(方法名称和方法签名),这些约定应通过支持类型来实现。接口类型定义可以定义和实现静态方法(请参见第8.4.3节),因为静态方法与接口类型本身而不是与该类型的任何值关联。


10
作为参考,CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields.它继续说,符合CLS的使用者可以拒绝这些类型的接口。大约一年前,我尝试在接口上调用静态方法,而C#编译器无法对其进行编译。
Christopher Currens,2013年

@ChristopherCurrens进一步介绍了CLS:Common Language Specification (CLS) is a set of basic language features that .Net Languages needed.... When there is a situation to communicate Objects written in different .Net Complaint languages , those objects must expose the features that are common to all the languages. 如果CLS是关于跨.NET语言的互操作性,并且C#不允许接口上有静态成员,则CLS也将禁止它们,以确保库中可以从C#调用其他.NET语言。
西蒙·图西

17

您可以在c#8中定义静态方法,但必须为其声明一个默认主体。

    public interface IMyInterface
    {
          static string GetHello() =>  "Default Hello from interface" ;
          static void WriteWorld() => Console.WriteLine("Writing World from interface");
    }

或者,如果您不想使用任何默认主体,则只需引发一个异常:

    public interface IMyInterface
    {
          static string GetHello() =>  throw new NotImplementedException() ;
          static void WriteWorld() => throw new NotImplementedException();
    }

界面中的静态成员似乎毫无用处,因为您无法通过界面实例访问它们。至少在C#8
帕维尔Sapehin

3
从接口实现的角度来看,您的权利。这是没有用的。但是这样至少可以确保在使用此接口的每个类上都有一个已实现的方法。(这是接口的可选实现)
AliReza

5

您可以通过反射来调用它:

MyInterface.GetType().InvokeMember("StaticMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);

5
如果没有MyInterface的实例,则可以使用“ typeOf(MyInterface)”代替“ myInterface.GetType()”。
RenniePet

当时似乎是个好主意,我可能会继续通过反思来做到这一点,但有一点警告:如果对程序进行混淆以致于将方法StaticMethod重命名,这确实会带来更多问题。
RenniePet

1
@RenniePet:您可以使用nameof(StaticMethod)来部分地处理StaticMethod重命名。根据重命名的方式,它可能会对混淆器有所帮助。如果以这种方式进行操作,则至少会看到编译时错误。
布伦特·里滕豪斯

对于这种情况,思考太极端了
Stepan Ivanenko

3

C#“十”将允许接口上的静态成员以及角色。这是向前迈出的一大步,它将允许通用运算符重载,而无需使用任何反射。这是一个示例代码片段,它使用经典的monoid示例工作,只是说“可以添加的内容”的行话。直接取自Mads Torgersen:C#进入未来

interface IMonoid<T>
{
    static T Zero { get; }
    static T operator +(T t1, T t2);
}

public static T AddAll<T>(T[] ts) where T : IMonoid<T>
{
    T result = T.Zero;
    foreach (T t in ts) { result += t; }
    return result;
}

role IntAddMonoid extends int : IMonoid<int>
{
    public static int Zero => 0;
}

IntAddMonoid[] values = new int[] {1, 2, 4, 8, 16, 32};
int sixtyThree = AddAll<IntAddMonoid>(values); // == 63

其他资源:

Jeremy Bytes:C#8接口静态成员

编辑

这篇最初声明的接口静态成员将在C#8.0中添加,这是不正确的,我在视频中误解了Mads Torgersen的话。官方的C#8.0指南还没有讨论静态接口成员,但是很明显,他们已经从事了很长时间了。


1

关于为什么您不能在接口上使用静态方法:为什么C#不允许静态方法实现接口?

但是,我建议删除静态方法,转而使用实例方法。如果无法做到这一点,则可以将静态方法调用包装在实例方法内部,然后可以为其创建接口并从中运行单元测试。

public static class MyStaticClass
{
    public static void MyStaticMethod()
    {...}
}

public interface IStaticWrapper
{
    void MyMethod();
}

public class MyClass : IStaticWrapper
{
    public void MyMethod()
    {
        MyStaticClass.MyStaticMethod();
    }
}

与仅使用接口相比,将接口与静态类一起使用有什么优势?
塞伦

1

C#8允许接口上有静态成员

从C#8.0开始,接口可以为成员定义默认的实现。它还可以定义静态成员,以便为通用功能提供单个实现。

接口(C#参考)

例如

public interface IGetSomething
{
    public static string Something = "something";
}

var something = IGetSomething.Something;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.