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

Jon*_*Jon 76 .net c# interface

我有一个第三方C++ DLL,我从C#调用.

这些方法是静态的.

我想抽象它来做一些单元测试,所以我创建了一个带有静态方法的接口,但现在我的程序错误:

修饰符'static'对此项无效

MyMethod cannot be accessed with an instance reference; qualify it with a type name instead
Run Code Online (Sandbox Code Playgroud)

我怎样才能实现这种抽象?

我的代码看起来像这样

private IInterfaceWithStaticMethods MyInterface;

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

public void MyMethod()
{
  MyInterface.StaticMethod();
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*rod 90

接口不能有静态成员,静态方法不能用作接口方法的实现.

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

public interface IMyInterface
{
    void MyMethod();
}

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

    void IMyInterface.MyMethod()
    {
        MyClass.MyMethod();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 对于想知道为什么有人想要这样做的人来说,在为实现静态方法的遗留代码编写单元/集成测试时特别有用. (8认同)
  • @gware 不是我建议解决这个问题的方法 - 尝试将内存中的数据结构注入到非静态类的构造函数中。另外,您可以让常规属性访问静态字段(也不推荐)。 (2认同)

Ali*_*eza 41

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

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

或者如果您不想拥有任何默认主体,只需抛出异常:

    public interface IMyInterface
    {
          static string GetHello() =>  throw new NotImplementedException() ;
          static void WriteWorld() => throw new NotImplementedException();
    }
Run Code Online (Sandbox Code Playgroud)

  • 接口中的静态成员似乎毫无用处,因为您无法通过接口实例访问它们。至少在 C# 8 中是这样。 (6认同)
  • 作为接口实现的观点,你是对的。这是没用的。但这样至少您可以确保使用此接口的每个类都有一个实现的方法。(这是接口的一种可选实现) (5认同)

dav*_*soa 39

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

我建议您按照当前的方式创建界面,但不要使用static关键字.然后创建一个StaticIInterface实现接口的类并调用静态C++方法.要进行单元测试,创建另一个类FakeIInterface,它也实现了接口,但是你需要处理单元测试.

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

  • -1表示"接口是合同,而不是实现."这是真的,但完全不相关(*非sequitur*),因为*静态方法不是实现本身的一部分* - 根据定义,实现是基于*data*,对于静态成员而言,*不可访问*.`接口类型定义可以定义和实现静态方法(参见§8.4.3),因为静态方法与接口类型本身相关联,而不是与类型的任何值相关联. - 请记住`static`成员通常是*实用方法*. (51认同)
  • 我理解并同意你的陈述,我觉得你的评论也是重要的背景。虽然。在设计接口时,应该将其视为一种契约,这意味着静态方法不适用。我想我应该把它留在那里来帮助一些人理解界面的目的。社区是否认为应该删除它? (3认同)
  • 我部分同意“接口是契约,而不是实现”是无用的,有时上下文化确实有帮助。我完全同意`静态方法不是实现本身的一部分`,静态方法**有**一个实现,只有在另一个方法的实现中用作实现时,它们才成为实现的一部分。然而,我的字典是基于所学的,据我所知,术语确实因编程语言而异。静态方法不能是接口,因为无论如何只能有 1 个实现。 (2认同)
  • @vaxquis - 恕我直言,如果这句话被改写,“它是一个契约”将是相关的:“接口是一个契约*对于实例*”。静态成员是类型的一部分;这个改写的句子(正确地)表明它们在实例合同中没有任何意义。所以我认为问题只是措辞不精确,而不是不合逻辑。 (2认同)

lep*_*pie 17

静态成员在CLR中完全合法,而不是C#.

你可以在IL中实现一些粘合剂来链接实现细节.

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

参见:8.9.4接口类型定义ECMA-335.

接口类型必然是不完整的,因为它们没有说明接口类型值的表示.因此,接口类型定义不应提供接口类型值(即实例字段)的字段定义,尽管它可以声明静态字段(参见§8.4.3).

类似地,接口类型定义不应为其类型的值提供任何方法的实现.但是,接口类型定义可以 - 并且通常会定义方法合同(方法名称和方法签名),这些合同应由支持类型实现.接口类型定义可以定义和实现静态方法(参见§8.4.3),因为静态方法与接口类型本身相关联,而不是与类型的任何值相关联.

  • 作为参考,`CLS规则19:符合CLS的接口不应定义静态方法,也不应定义字段.接着说,符合CLS的消费者可以拒绝这些类型的接口.我在一年前尝试在接口上调用静态方法,而C#编译器不会编译它. (8认同)

Bas*_*sem 10

从 C# 11 和 .NET 7 开始,接口中可以包含静态成员,而无需提供默认实现。

以下是定义接口的方法。

interface IDoSomething
{
    static abstract void DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

下面是如何让一个类实现这个接口。

class DoerImplementation : IDoSomething
{
    public static void DoSomething()
    {
        // Method implementation goes here
    }
}
Run Code Online (Sandbox Code Playgroud)

您可能会使用像这样的泛型类型参数来指定要使用的实现。

class MyClass
{
    void MyMethod<T>() where T : IDoSomething
    {
        T.DoSomething();
    }

    void MyOtherMethod()
    {
        MyMethod<DoerImplementation>();
    }
}
Run Code Online (Sandbox Code Playgroud)

有关接口中静态虚拟成员的详细信息,请参阅此Microsoft Learn 文章


Mel*_*per 8

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

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

接口(C# 参考)

例如

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

var something = IGetSomething.Something;
Run Code Online (Sandbox Code Playgroud)

  • 这些是为了方便而存在于接口中的静态成员(用于帮助默认接口实现),但它们不是实际“接口”的一部分,并且不应用于接口的实现。 (2认同)

Wim*_*aer 7

这篇文章很旧,但自上一篇相关文章以来,C# 发生了变化。

C#8 预习

静态方法不是一个东西

C#10/11 预习

可以定义静态方法/属性,但必须实现:

public interface MyInterfaceWithStaticMethod 
{
    public static String HelloWorld() => "Hello world";
}
Run Code Online (Sandbox Code Playgroud)

C#10/11

在撰写本文时,已知静态接口的功能将出现在 C# 中,但我不清楚具体版本。

根据本文,您现在可以通过 C#10 .NET6 预览来尝试“接口中的静态抽象成员”

另一方面,根据本文,它将仅在 C#11 中发布。


Tam*_*dus 6

他们正在考虑在未来的 C# 版本中添加其中一些特性,称为 C#“十”。这些理论特性可能允许接口上的静态成员以及角色。这将是向前迈出的一大步,它也将允许泛型运算符重载,而无需使用任何反射。这是一个示例片段,它是如何计划工作的,使用经典的 monoid 示例,这只是说“可以添加的东西”的行话。直接取自Mads Torgersen:C# into the Future

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
Run Code Online (Sandbox Code Playgroud)

其他资源:

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

编辑

这篇文章原先声明接口静态成员会在C#8.0中加入,这不是真的,我误解了视频中Mads Torgersen的话。官方的 C# 8.0 指南还没有讨论静态接口成员,但很明显他们已经研究了很长时间。


Joh*_*ner 5

你可以用反射来调用它:

MyInterface.GetType().InvokeMember("StaticMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
Run Code Online (Sandbox Code Playgroud)

  • 如果你没有MyInterface的实例,你可以使用"typeOf(MyInterface)"而不是"myInterface.GetType()". (5认同)