繁体   English   中英

如何在接口上实现 static 方法?

[英]How can I implement static methods on an interface?

我有一个来自 C# 的第三方 C++ DLL。

方法是 static。

我想把它抽象出来做一些单元测试,所以我创建了一个带有 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();
}

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

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

public interface IMyInterface
{
    void MyMethod();
}

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

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

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

您不能在 C# 中的接口上定义静态成员。 接口是实例的契约。

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

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

可以在 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();
    }

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

您可以在 IL 中实现一些胶水来链接实现细节。

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

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

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

同样,接口类型定义不应为其类型的值提供任何方法的实现。 然而,接口类型定义可以——而且通常确实——定义应由支持类型实现的方法契约(方法名称和方法签名)。 接口类型定义可以定义和实现静态方法(参见第 8.4.3 节),因为静态方法与接口类型本身相关联,而不是与该类型的任何值相关联。

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

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

接口(C# 参考)

例如

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

var something = IGetSomething.Something;

他们正在考虑在未来的 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

其他资源:

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

编辑

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

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

预 C#8

Static 方法不是一回事

预 C#10/11

Static 方法/属性可以定义但必须实现:

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

C#10/11

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

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

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

您可以通过反射调用它:

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

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

但是,我建议删除静态方法以支持实例方法。 如果这是不可能的,那么您可以将静态方法调用包装在一个实例方法中,然后您可以为此创建一个接口并从中运行单元测试。

IE

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

public interface IStaticWrapper
{
    void MyMethod();
}

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

除了编译器认为我不应该这样做之外,我没有看到其他问题。 C# 不能从多个基类继承,当你习惯这样做时真的很糟糕,正面你可以做几个接口,所以我滥用它来潜入我需要的功能;-)

您应该检查 null 等,但是这里是一个简化版本,它实现了 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 version 8,你有默认的实现,所以“潘多拉的盒子是打开的”

您可以简单地使用:

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

所以你使用:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM