在接口中声明成员函数

12

首先,我对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#中是否有一种优雅的方式可以使用接口来实现这个功能。


10
句号:接口永远不能定义实现。 - Kirk Woll
1
接口不能包含代码。它们仅定义实现类必须提供的成员。 - Matt
请问您能否明确说明您想要做什么? - Philip Tenn
我编辑了我的问题。对于一开始的表述不够清晰,抱歉。 - vanna
说句实话,你还没有描述你要解决的实际问题;你只是在问如何用C#写C++。 - Austin Salonen
显示剩余3条评论
9个回答

12

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");
    }
}

现在让我们来到你的“final”组合类。它将从SomeBaseClass派生,并通过集成ConcreteMyInterface的功能来实现IMyInterface

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");
        }
    }
}

我不明白定义一个抽象类的意义所在。为什么不直接让ConcreteMyInterface直接实现IMyInterface呢? - undefined
1
它允许抽象类声明调用抽象方法的代码。在这个例子中,MyAction调用了抽象方法Function。正如MyAction中的注释所建议的那样,可能还有很多实现类会继承的代码。 - undefined

11

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

然而,另一种替代方法是创建一个接口和静态类。您可以使用该接口创建扩展方法来放置您的逻辑。

public interface IMyInterface {
    void Function();
}

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

这里的主要缺点是更多类型,这会降低可维护性,还有发现性不足。


3
感谢提到使用扩展方法在接口中实现功能的常被忽视的替代方案。+1 - Sergey Kalinichenko
3
我担心一个全新的C#用户可能不理解扩展方法和真实实例方法之间的区别,如果你不了解它们的不同,可能会引起问题。 - Servy
@Servy - 我同意 - 我认为扩展方法不应该是首选的设计方法。最好留给那些需要在无法直接修改的类上扩展行为的情况。 - RJ Lohan
我完全同意 - 这就是为什么我特别提到这种方法有缺点。考虑到OP的要求(使用接口,但提供逻辑),这可能是一个合理的方法。 - Reed Copsey
我喜欢这种方法。如果我有一个保证拥有一些成员的接口,那么公开给定这些成员的常见行为是有意义的,特别是如果它们都是公共的。我认为接口的整个重点是摆脱多重继承... - jv-dev

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

输出:

Hello World

1
这是一个令人困惑的领域,不是我建议给自称初学者的设计方法。扩展方法更适合用于为您无法控制的类提供额外的功能,而不是作为一般设计方法。 - RJ Lohan
@RJLohan:实际上,与LINQ相关的几乎所有内容都是按照这种方式设计的。有一个由类实现的最小接口(IEnumerable<T>),以及一个包含数十个扩展方法的静态类(Enumerable)。当然,在应用此设计之前必须理解其局限性,我认为这对于初学者可能不是最佳选择。 - dtb
那个例子支持了我的观点 - LINQ 的设计是为了在类上提供行为,而这些类的开发者(在这种情况下是微软)无法控制(在这种情况下是任何实现 IEnumerable 接口的人编写的所有代码)。 - RJ Lohan

2

接口无法实现任何行为,它们只是约定。如果您想在定义合同时实现某些逻辑,则可以使用抽象类。


1
我知道。这就是我的问题,如何克服这个了解我的需求的情况。 - vanna
2
@vanna 实际上,你从未提出问题。你只是发表了一些声明。隐含的问题是,“如何定义接口中方法的实现?”而(正确的)答案是,你不能这样做。如果你有一个需要解决的问题,并想知道如何解决它,请描述该问题,而不是询问如何在接口中定义方法。 - Servy

2
为了达到这个目的,您需要定义抽象类。您可以提供默认实现,也可以将实现留给派生类。
如果派生类想要覆盖某些内容,他们总是可以这样做。这使他们能够在使用基础功能的同时进行所需的更改。

1

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
    }
}

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

0

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

从您上面的陈述中:

我可以使用抽象类代替接口,但那样我就无法继承其他类了

我认为您正在遇到“为什么C#不支持多重继承”的问题。

这里有一篇CodeProject文章Simulated Multiple Inheritance for C#。 您应该能够按照这个模式来实现C#简单继承模型的解决方法。


@AustinSalonen 很好的观点,谢谢。我根据你的评论编辑了我的答案。 - Philip Tenn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接