10

首先,我对 C# 很陌生。我想要一个接口声明一个成员函数,如下面的代码

interface IMyInterface {
    void MyAction() {
        // do stuff depending on the output of function()
    }
    void Function();
}

这里Function是纯虚拟的,应该由IMyInterface. 我可以使用抽象类而不是接口,但是我不能从其他类继承......例如,MyAction递归搜索目录中的文件并应用于Function找到的任何文件以使我的示例清晰。

如何改变我的设计以克服接口无法实现类的限制?

编辑:在 C++ 中,我会使用这样的模板

template<class A>
static void MyAction(const A& a) {
    // do stuff depending on the output of A::Function()
};

class MyClass {
    void Function();
};

我想知道是否有一种优雅的方法可以使用 C# 中的接口来做到这一点。

4

9 回答 9

11

直接处理这个问题的唯一方法是使用抽象类,因为接口不能包含任何形式的“逻辑”,而仅仅是一个契约。

然而,另一种选择是创建一个接口和一个静态类。然后,您可以使用该接口将您的逻辑放在扩展方法中。

public interface IMyInterface {
    void Function();
}

public static class MyInterfaceExtensions {
    public static void MyAction(this IMyInterface object)
    {
       // use object.Function() as needed
    }
}

这里的主要缺点是类型更多,这降低了可维护性,并且缺乏可发现性。

于 2013-01-24T21:27:49.283 回答
11

在 C# 中,您没有多重继承。您可以通过使用composition来规避此限制。

像这样定义你的接口(Function这里不需要定义):

public interface IMyInterface
{
    void MyAction();
}

用一个抽象声明一个抽象类Function并实现这个接口:

public abstract class MyInterfaceBase : IMyInterface
{
    public void MyAction()
    {
        // Do stuff depending on the output of Function().
        Function();
    }

    protected abstract void Function();
}

从这个抽象类中,您可以派生一个具体的实现。这还不是您的“最终”课程,但它将用于编写它。

public class ConcreteMyInterface : MyInterfaceBase
{
    protected override void Function()
    {
        Console.WriteLine("hello");
    }
}

现在让我们来看看你的“最终”组成的课程。它将源自SomeBaseClassIMyInterface通过集成以下功能来实现ConcreteMyInterface

public class SomeBaseClass
{
}

public class MyComposedClass : SomeBaseClass, IMyInterface
{
    private readonly IMyInterface _myInterface = new ConcreteMyInterface();

    public void MyAction()
    {
        _myInterface.MyAction();
    }
}

更新

在 C# 中,您可以声明本地类。这更接近于多重继承,因为您可以在组合类中派生所有内容。

public class MyComposedClass : SomeBaseClass, IMyInterface
{
    private readonly IMyInterface _myInterface = new ConcreteMyInterface();

    public void MyAction()
    {
        _myInterface.MyAction();
    }

    private class ConcreteMyInterface : MyInterfaceBase
    {
        protected override void Function()
        {
            Console.WriteLine("hello");
        }
    }
}
于 2013-01-24T22:13:58.227 回答
7

您可以定义MyAction扩展方法

public interface IMyInterface
{
   void Function();
}

public static class MyInterfaceExtensions
{
    public static void MyAction(this IMyInterface obj)
    {
        obj.Function();
    }
}

例子:

public class HelloWorld : IMyInterface
{
    public void Function()
    {
        Console.WriteLine("Hello World");
    }

    public static void Main(string[] args)
    {
        new HelloWorld().MyAction();
    }
} 

输出:

你好世界
于 2013-01-24T21:28:49.480 回答
2

接口不能实现任何行为,它们只是合同。如果你想在定义合约时实现一些逻辑,你可以使用抽象类。

于 2013-01-24T21:25:52.743 回答
2

为了这个目的 。你需要定义抽象类。您可以提供默认实现,也可以将实现留给派生类。

如果派生类想要覆盖某些东西,他们总是可以这样做。这使他们可以灵活地使用 base 以及他们想要覆盖的更改。

于 2013-01-24T21:37:19.573 回答
1

在接口中声明函数的接口(签名和返回类型),然后创建一个定义为实现该接口的抽象类,并在抽象类中实现一个基本的默认实现。然后,创建从抽象类继承的其他具体类,但在必要时,用不同的实现覆盖抽象类的基本实现。

于 2013-01-24T21:30:10.207 回答
1

最好通过分离外部行为来克服这类问题;MyAction在这种情况下,从内部实现;MyFunction.

这里的重点是理解什么应该是这个类和其他类之间的接口/契约的一部分,以及什么应该是该契约的实现的一部分。

在这里,定义了这个对象和它的消费者之间的契约;

interface IMyInterface
{
    void MyAction();
}

现在,一个实现这个接口的基类,也执行一个特定的行为;

abstract class BaseClass : IMyInterface
{
    public void MyAction()
    {
        // do some commmon action

        // call derived implementation to deal with the outcome
    }

    protected abstract void MyFunction();
}

最后,一个具体的实现,它以某种特定的方式处理 MyFunction 的结果;

class ConcreteClass : BaseClass
{
    protected override void MyFunction()
    {
         // concrete implementation here
    }
}
于 2013-01-24T21:35:43.887 回答
0

接口是契约,不能包含实现。

从你上面的陈述:

我可以使用抽象类而不是接口,但是我不能从其他类继承

我相信您遇到了“为什么 C# 不支持多重继承”的问题。

这是一篇关于C# 的模拟多重继承的 CodeProject 文章。您应该能够遵循此模式来解决 C# 的简单继承模型。

于 2013-01-24T21:30:56.753 回答
0

这是 C# 8.0 的建议功能:

interface IA
{
    void M() { WriteLine("IA.M"); }
}

class C : IA { } // OK

IA i = new C();
i.M(); // prints "IA.M"`

https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md

于 2017-06-23T05:06:55.537 回答