目录
介绍
接口的今天
默认接口方法
钻石问题
接口中的修饰符
总结
参考
介绍众所周知,C#8.0几天前就已发布。这个版本的C#提供了许多令人兴奋的功能。接口也发生了大量的变化,因此在本文中,我们将尝试探索新功能,并尝试了解如何在项目中使用它们。
接口的今天作为开发人员,我们都使用接口,无论是创建松散耦合的组件还是定义应由具体类实现的契约。今天的接口永远不会出现方法身体,没有修饰符。实现者类负责提供方法体并为其分配一些修饰符。如果类没有实现该方法,编译器会捕获它并提供错误,说我们需要实现该接口。
在整个应用程序中,我们将使用Logger类的示例并相应地进行更改。
using System;
public interface ILogger
{
void Log(string Info);
}
public class TextLogger : ILogger
{
public void Log(string Info)=> Console.Write("In base Logger");
}
我们定义了一个拥有一个Log()方法的ILogger接口。我们有一个叫做实现接口ILogger的TextLogger类。考虑到当前的设计状态,这是完全正常的。现在,当我们想要扩展ILogger并且需要在其中添加更多信息时,会出现问题,如下所示:
public interface ILogger
{
void Log(string info);
void Log(string typeofInformation,string info)
}
现在我们将遇到一个问题,因为这个新方法必须由使用此接口的类实现,编译器将向我们显示错误,直到完成如下所示:
现在考虑到这个接口被多个类使用,这将打破许多变化,并且根据使用此接口的位置在实现中进行这些更改将非常痛苦。我们需要实现这个方法,以便我们的代码编译。为了克服这个问题,C#8想出了接口中的默认方法。
默认接口方法在C#8.0中获得此功能的主要原因是为接口方法提供默认实现,那么我们该如何做呢?我们来看下面的例子:
using System;
public interface ILogger
{ void Log(string info);
//Default implementation void LogInfo(string typeofInformation,
//string info)=>Console.Write(typeofInformation+ " " + info);
}
public class TextLogger : ILogger {
public void Log(string info)=> Console.Write("In base Logger");
}
在这里,我们可以在接口本身看到,我们已经为该函数提供了实现。这里,我们的类TextLogger不需要实现这个方法,也不会有任何编译时错误。现在,为了在我们的应用程序中使用此接口,让我们改变我们的main方法,让我们看看我们如何使用它。
class Program
{
static void Main(string[] args)
{
ILogger _logger = new TextLogger();
_logger.LogInfo("Test","test"); // It will call the Default method of the interface
}
}}
我们需要检查的另一件事是,只有在将类作为接口进行上下文处理时,默认方法才有效。如果我们不这样做,那么默认方法实现将无法使用:
在上面的截图中,我们可以看到从类创建对象时,我们可以看到默认方法无法使用。如果我们仔细研究这个特性,我们可以看到这可能导致众所周知的多重继承问题,这个问题被称为钻石问题。根据设计,C#不会遇到任何问题,因为类和接口没有多重继承,没有方法的实现,但是使用默认方法,这将会改变。让我们看看如何在C#8.0中处理它。
钻石问题钻石问题是语言中的一个大问题,因为C#类不支持这种多重继承的结果,但是接口可以在某种程度上引入这个问题。让我们看看C#如何处理它们。下图说明了钻石问题:
上图很好地描述了钻石问题。现在,让我们看看默认接口如何产生这个问题以及C#如何处理它。让我们设计如下接口:
Interface First
{
void WritetoConsole() => Console.Write("In First");
}
interface Second:First{
void First.WritetoConsole()=>Console.Write("In Second");
}
interface Third:First{
void First.WritetoConsole()=>Console.Write("In Third");
}
class FinalClass : Second,Third
{
}
在编写此代码时,我们将遇到编译时错误:
错误消息将是接口成员' First.WritetoConsole()'没有最具体的实现。 “Second.First.WritetoConsole()”和“ Third.First.WritetoConsole()”都不是最特殊的。(CS8705)[ DefaultInterfaceDemo]。为了解决错误本身所描述的这个问题,我们需要在执行时提供最具体的覆盖,Dotnet设计团队已经具体告诉它,如下,“接口成员的类实现应该总是赢得接口中的默认实现,即使它是从基类继承的。默认实现总是只在类没有任何成员实现时才会回滚“。让我们看看我们如何提供默认实现并解决这个钻石问题。
using System;
interface First
{
void WritetoConsole() => Console.Write("In First");
}
interface Second:First{
void First.WritetoConsole()=>Console.Write("In Second");
}
interface Third:First{
void First.WritetoConsole()=>Console.Write("In Third");
}
class FinalClass : Second,Third
{
void First.WritetoConsole(){
Console.Write("From Final class");
}
}
接口中的修饰符
传统上,直到C#8.0的到来,我们无法在接口中使用修饰符。在C#8.0中,我们可以使用它们,至今修饰符像private,protected,internal,public和virtual是允许的。在设计上,所有默认接口的方法都是virtual,除非我们将它们设置为private或sealed。没有正文的所有成员在默认情况下都被视为抽象,因此必须在具体类中实现。
using System;
interface IInterfaceModifiers
{
//By Default default method is private
virtual void DefaultMethod()=>Console.WriteLine("Default method");
//Private Default Method
private void privatedefaultmethod()=>Console.WriteLine(" private Default method");
//Protected Default Method
protected void ProtectedDefaultMethod()=>Console.WriteLine(" protected Default method");
// Public Default Method
public void PublicDefaultMethod()=>Console.WriteLine(" public Default method");
virtual void VirtualDefaultMethod()=>Console.WriteLine("Virtual Default method");
abstract void AbstractDefaultMethod();
}
class InterfaceModifierDemo : IInterfaceModifiers
{
public void AbstractDefaultMethod() => Console.WriteLine("Abstract virtual method");}
namespace DeaultInterfaceDemo
{
class Program
{
static void Main(string[] args)
{
IInterfaceModifiers i= new InterfaceModifierDemo();
i.AbstractDefaultMethod();
i.DefaultMethod();
i.PublicDefaultMethod();
i.VirtualDefaultMethod();
}
}
}
当我们运行上面的代码时,我们可以在控制台上看到以下输出:
Abstract virtual method
Default method
public Default method
Virtual Default method
除此之外,我在这个例子中遇到的观察很少。当我们使一个方法成为virtual时,我们可以在接口本身中重写该方法,并且我们不能在实现类中重写它。当我们创建一个protected方法时,它在继承接口中可用,而不是实现类。默认情况下,接口的成员是abstract,这使得实现类必须正确实现它们。
总结我们已经看到了C#8.0中最具争议但最激动人心的特性。它将改变我们在设计中使用接口的方式,这肯定会帮助开发人员产生更少的重大变化,但它也会提出自己的性能和设计视角的挑战。要添加的另一件事是,此功能目前在.NET框架中不可用,但包含在.NET Core和Core CLR以及MONO中。
参考- https://www.infoq.com/articles/default-interface-methods-cs8/
- https://www.codejourney.net/2019/02/csharp-8-default-interface-methods/
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-04-19.md
原文地址:https://www.codeproject.com/Articles/5161143/Csharp-8-Interfaces