区分 C# 中的显式和隐式接口实现
介绍
C# 接口成员可以显式或隐式实现。在大多数情况下,隐式实现成员是最简洁、最方便的选择。但是,有时隐式实现会导致歧义或创建过多的可访问成员。可以为显式接口实现成员,以避免在使用级别出现歧义的成员访问。本文将讨论显式实现和隐式实现之间的区别,以及显式接口成员实现如何解决歧义。
主要区别
显式实现和隐式实现之间的主要区别在于,隐式实现必须在实现的具体类型上公开对成员的访问。这意味着该成员将在具体类型上可用。成员的显式实现不允许访问修饰符,并且始终在具体类型上具有默认成员访问权限。实际上,这将始终是private,因此当对象转换为具体类型时,显式实现的成员将无法访问。
隐式实现
隐式实现往往更常见,使用起来也更方便。它们更简洁,并且任何使用具体类型的操作都会暴露成员的实现。隐式实现在成员名称之前不包含要实现的接口的名称,因此编译器会推断这一点。成员将作为公共成员暴露,并且在对象转换为具体类型时可以访问。
考虑这些接口:
public interface IGetsMessage1
{
string GetMessage();
}
public interface IGetsMessage2
{
string GetMessage();
}
下面的代码以隐式的方式实现成员。
public class ImplicitImplementationBase : IGetsMessage1
{
public string GetMessage()
{
return nameof(IGetsMessage1);
}
}
public class ImplicitImplementation : ImplicitImplementationBase, IGetsMessage2
{
internal new string GetMessage()
{
return nameof(IGetsMessage2);
}
}
歧义
在具体类型ImplicitImplementation上调用GetMessage()时,可能不清楚将调用哪种方法。
考虑这个例子:
internal class Program
{
private static void Main(string[] args)
{
var implicitImplementation = new ImplicitImplementation();
Console.WriteLine(implicitImplementation.GetMessage());
}
}
如果在定义Program 的程序集中定义了ImplicitImplementation,则输出将为“IGetsMessage2”。但如果将类型移动到外部程序集,则将调用该方法的公共版本,结果将为“IGetsMessage1”。这是因为ImplicitImplementation类中的GetMessage()具有内部访问修饰符。为了解决这个问题,可以显式实现接口的成员。
明确实施
成员的显式实现需要接口名称和成员名称前的句点,如下所示。成员不会由具体类型公开,但可以在对象转换为接口时使用。“显式”一词指的是明确提及接口名称,因此可以清楚地知道正在实现哪个接口的成员。此外,使用显式实现可能更可取,以减少类型上公开的成员数量。
public class ExplicitImplementationBase : IGetsMessage1
{
string IGetsMessage1.GetMessage()
{
return nameof(IGetsMessage1);
}
}
public class ExplicitImplementation : ExplicitImplementationBase, IGetsMessage2
{
string IGetsMessage2.GetMessage()
{
return nameof(IGetsMessage2);
}
}
考虑这个例子:
internal class Program
{
private static void Main(string[] args)
{
var implicitImplementation = new ImplicitImplementation();
var getsMessage2 = (IGetsMessage2)implicitImplementation;
Console.WriteLine(getsMessage2.GetMessage());
var explicitImplementation = new ExplicitImplementation();
getsMessage2 = explicitImplementation;
Console.WriteLine(getsMessage2.GetMessage());
Console.ReadLine();
}
}
输出:
获取消息1
获取消息2
显式实现的版本调用专门属于 IGetsMessage2 的方法,而隐式实现的版本使用它找到的第一个公共版本,该版本位于ImplicitImplementationBase中,而不是ImplicitImplementation中。这就是显式实现如何消除成员访问歧义的方式。
ExplicitImplementation的用法与ImplicitImplementation不同。以下代码无法编译。
var explicitImplementation = new ExplicitImplementation();
Console.WriteLine(explicitImplementation.GetMessage());
会出现的错误信息是:
“ExplicitImplementation”不包含“GetMessage”的定义,并且找不到接受“ExplicitImplementation”类型第一个参数的可访问扩展方法“GetMessage”(您是否缺少使用指令或程序集引用?)
这是因为GetMessage()在具体类型上声明了默认成员访问 private。要访问成员,必须将对象显式转换为接口。这迫使用户消除成员的歧义。为了显式性,牺牲了便利性和简洁性。应该注意的是,出于这个原因,大多数 API 倾向于隐式实现。显式实现可能会引起混淆。当面对类型ExplicitImplementation时,无法看到该类型上哪些成员是可访问的。Intelliprompt 不会公开任何成员,并且可能不清楚该类的用途。
此代码将产生相同的结果,但只有显式版本才会强制用法明确无误。
internal class Program
{
private static void Main(string[] args)
{
var explicitImplementation = new ExplicitImplementation();
var test1 = (IGetsMessage1)explicitImplementation;
var test2 = (IGetsMessage2)explicitImplementation;
Console.WriteLine(test1.GetMessage());
Console.WriteLine(test2.GetMessage());
var implicitImplementation = new ImplicitImplementation();
test1 = implicitImplementation as IGetsMessage1;
test2 = implicitImplementation as IGetsMessage2;
Console.WriteLine(test1.GetMessage());
Console.WriteLine(test2.GetMessage());
Console.ReadLine();
}
}
输出
获取消息1
获取消息2
获取消息1
获取消息2
结论
要区分显式和隐式接口实现,请在实现的成员声明之前检查接口名称。显式实现可以消除成员访问歧义。显式和隐式实现之间的选择会改变实现类的用法。这使得选择成为编码风格的问题。了解差异很重要,这样就可以在团队中讨论这个问题,并了解如何使用显式实现接口成员的具体类型。与往常一样,一致性是关键,任何使用 API 的人都应该获得使用示例和如何使用类型的说明。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~