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

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

方法是静态的。

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

“ static”修饰符对此项无效

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();
}
277259 次浏览

在 C # 中不能在接口上定义静态成员,接口是一个契约 例如

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

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

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

您可以使用一个显式的接口实现:

public interface IMyInterface
{
void MyMethod();
}


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


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

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

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

但是,我建议移除静态方法,使其有利于实例方法。如果这是不可能的,那么您可以将静态方法调用封装在实例方法内部,然后您可以为其创建一个接口并从该接口运行您的单元测试。

也就是说

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


public interface IStaticWrapper
{
void MyMethod();
}


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

你可以通过反思来调用它:

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

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

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

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

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

接口类型必然是不完整的,因为它们什么也不说 关于接口类型的值的表示形式 因此,接口类型定义不应提供字段 接口类型的值的定义(例如,实例字段) , 尽管它可以声明静态字段(参见8.4.3)。

类似地,接口类型定义不应提供 对其类型的值的任何方法的 接口类型定义可以定义方法契约,而且通常确实可以定义方法契约 (方法名称和方法签名) 接口类型定义可以定义和 实现静态方法(见8.4.3) ,因为静态方法是 与接口类型本身关联,而不是与任何值关联 类型。

可以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 # 11中也可以有 static abstract成员,而抽象成员不需要默认实现

public interface IMyInterface
{
static abstract string GetHello();
static abstract void WriteWorld();
}

关于静态摘要的更多信息

他们正在考虑在未来的 C # 版本中添加一些这样的特性,称为 C # “ Ten”。这些理论特性可能允许在接口上使用静态成员,以及角色。这将是一个巨大的进步,它也将允许使用通用运算符重载,而不需要任何反射。下面是一个示例代码片段,使用经典的 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

额外资源:

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

剪辑

这篇文章原来说的界面静态成员将添加在 C # 8.0,这是不正确的,我误解了 Mads Torgersen 的话在视频中。C # 8.0官方指南还没有谈到静态接口成员,但是很明显他们已经在这方面工作了很长时间了。

C # 8允许在接口上使用静态成员

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

接口(C # 引用)

例如。

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


var something = IGetSomething.Something;

除了编译器认为我不应该这样做,我没有看到其他问题。C # 不能从一个以上的基类继承,当你习惯于这样做的时候,真的很扫兴,在前面你可以做几个接口,所以我滥用它来偷偷地插入我需要的特性; -)

您应该检查空等,但这里是一个简化版本,实现 Parse 从 Web 服务或数据库获取类

/// <summary>
/// Implements parse
/// </summary>
/// <typeparam name="T">the type to parse</typeparam>
public interface IParse<T>
{
/// <summary>
/// implements parse from string to type
/// </summary>
/// <param name="text">value to parse</param>
/// <returns></returns>
static T Parse(string text)=>JsonConvert.DeserializeObject<T>(text, settings:new JsonSerializerSettings() { ConstructorHandling= ConstructorHandling.AllowNonPublicDefaultConstructor });


/// <summary>
/// implements parse from string to type
/// </summary>
/// <param name="text">value to parse</param>
/// <param name="settings">the settings to us</param>
/// <returns></returns>
static T Parse(string text, JsonSerializerSettings settings) =>JsonConvert.DeserializeObject<T>(text, settings);
}

然后调用接口 在以 List 作为返回值的代码中。

这里有一个片段,我从数据库中读取 JSON,将其填充到已经实现了 JSON 的类型中

//some plugging code


using (var reader = cmd.ExecuteReader(behavior: CommandBehavior.CloseConnection | CommandBehavior.SingleResult))
{
if (reader.HasRows)
{
while (reader.Read())
{
rows++;
try
{
var json = reader.GetString(0);


result.Add(IParse<T>.Parse(json));
}
catch
{
failed++;
}
}
}
}
//other plugging code

在 > ver 版本8中,您有默认的实现,因此“ Pandora’s box is open”

你可以简单地使用:

     public interface MyInterface<T>
{
T Method();
}
     

public class MyClass<T> : MyInterface<T>
{
public T Method() => //Your implementation
}

所以你用:

    var result = new MyClass<T>().Method();

静态类是一个不需要实例化的类。但是 C # 允许这样的事情发生,因为对我来说它很简单并且可以工作(最重要的是)。 当然,您需要避免在 Method ()中实现任何在类中更改信息的内容,因为在这个调用之后,类将会消失(I Hope)。

这篇文章已经很老了,但是自从上一篇相关的文章以来,C # 发生了一些变化。

前 C # 8

静态方法不是一个东西

C # 10/11之前

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

public interface MyInterfaceWithStaticMethod
{
public static String HelloWorld() => "Hello world";
}

C # 10/11

在编写本文的时候,我们已经知道 C # 将具备静态接口的特性,但是我还不清楚它的版本。

根据 这篇文章,你现在可以在接口中尝试“静态抽象成员”,预览 C # 10.NET6

另一方面,根据 这篇文章,它将只发布在 C # 11。