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

时间:2012-02-23 14:45:44

标签: c# .net interface

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

方法是静态的。

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

  

修饰符'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();
}

9 个答案:

答案 0 :(得分:75)

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

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

public interface IMyInterface
{
    void MyMethod();
}

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

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

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

答案 1 :(得分:35)

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

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

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

答案 2 :(得分:19)

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

答案 3 :(得分:13)

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

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

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

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

  

接口类型必然是不完整的,因为它们什么都不说   关于接口类型值的表示。为了这   原因,接口类型定义不应提供字段   接口类型值的定义(即实例字段),   虽然它可以声明静态字段(见§8.4.3)。

     

同样,接口类型定义不应提供   关于其类型值的任何方法的实现。但是,一个   接口类型定义可以 - 通常也可以定义方法合同   (方法名称和方法签名)应由...实施   支持类型。接口类型定义可以定义和   实现静态方法(参见§8.4.3),因为静态方法是   与接口类型本身相关联而不是与任何值相关联   类型。

答案 4 :(得分:5)

你可以用反射调用它:

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

答案 5 :(得分:3)

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 interface static members

编辑

此帖子最初声明的接口静态成员将添加到 C#8.0 中,这是不正确的,我在视频中误解了Mads Torgersen的话。 The official C# 8.0 guide尚未谈论静态接口成员,但是很显然,他们已经在静态接口成员上工作了很长时间。

答案 6 :(得分:1)

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

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

interface (C# Reference)

例如

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

var something = IGetSomething.Something;

答案 7 :(得分:0)

至于为什么你不能在界面上使用静态方法:Why Doesn't C# Allow Static Methods to Implement an Interface?

但是,我建议删除静态方法,转而使用实例方法。如果那是不可能的,那么你可以在实例方法中包装静态方法调用,然后你可以为它创建一个接口并从那里运行你的单元测试。

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

public interface IStaticWrapper
{
    void MyMethod();
}

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

答案 8 :(得分:0)

除了编译器认为我不应该这样做之外,我没有看到其他问题。 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,你有默认的实现,所以“潘多拉的盒子是打开的”