VB.Net中接口的行为与其他语言不同

6

在Vb.Net中,接口的行为不同。下面是一个示例代码片段,其中IStudent接口具有一个名为SayHello的方法,该方法由Student类实现。访问限定符SayHello默认应该是Public。将访问限定符更改为Private不会破坏现有代码,仍然可以使用以下代码访问此私有方法:

Dim stdnt As IStudent = New Student
stdnt.SayHello()

访问修饰符确定类中成员的范围,私有成员仅可被所在类访问。但是,在这里,访问修饰符和封装的理论已经被打破。

  • .net为什么要设计成这样?
  • 访问修饰符和封装的概念真的被打破了吗?
  • .net框架如何内部处理这种情况?

提前致谢。

Module Module1

   Sub Main()
        Dim stdnt As IStudent = New Student
        stdnt.Name = "vimal"
        stdnt.SayHello()
   End Sub

End Module

Public Interface IStudent

   Property Name As String

   Sub SayHello()

End Interface

Public Class Student
   Implements IStudent

   Private Property Name As String Implements IStudent.Name

   Private Sub SayHello() Implements IStudent.SayHello
       Console.WriteLine("Say Hello!")
   End Sub

End Class
5个回答

4

来自MSDN

您可以使用私有成员来实现接口成员。当私有成员实现接口的成员时,即使在类的对象变量上直接不可用,该成员也通过接口可用。

在C#中,通过显式实现接口来实现这种行为,如下所示:

public interface IStudent {
    string Name { get; set; }
    void SayHello();
}

public class Student : IStudent {
    string IStudent.Name { get; set; }

    void IStudent.SayHello() {
        Console.WriteLine("Say Hello!");
    }
}

因此,如果您省略方法名称前面的IStudent。,它将会出错。我看到在VB语法中包含了接口名称。虽然我不知道这是否有任何影响。但是接口成员并不是私有的,因为接口本身不是私有的。它们有点像公共成员...


那么为什么C#的行为不同呢? - Vimal CK
我更新了我的答案,提供了C#的另一种选择,虽然我不能完全确定它是否是这样工作的。 - JLe
不,它不会。我发布的代码完美运行。然而,你不能在方法前面加上 private。但是你也不必指定 public,所以它介于 public 和 private 之间... - JLe
一个接口实现的类中的所有成员默认应该是公共的。这里的问题是,当修饰符更改为私有时,为什么成员仍然可以访问?请尝试不使用显式实现的C#代码片段,它不应该编译。 - Vimal CK
@VimalCK 重点是你展示的VB语法等同于C#中的显式实现。两种语言的代码片段是可比较的。如果你将C#代码更改为非显式实现,则需要同时更改VB代码,以使示例可比较。 - Servy

4

原帖作者通过TheBugGuys@Coverity.com向我提交了这个问题;我的答案在这里:

https://communities.coverity.com/blogs/development-testing-blog/2013/10/09/oct-9-posting-interface-behaves-differently-in-visual-basic

简要概括一下:

.NET为什么要以这种方式设计?

这个问题太过模糊。

C#和VB中的显式实现是否破坏了封装性?

没有。方法的隐私限制了方法名称的可访问域,而不是谁可以调用该方法。如果类的作者选择通过某种机制使私有方法可被调用,而不是通过查找名称来进行调用,那就是他们的选择。第三方除非使用私有反射等技术,否则无法替他们做出选择,这会破坏封装性。

.NET如何实现此功能?

有一个专门的元数据表用于显式接口实现。当CLR尝试确定哪个类(或结构)方法与哪个接口方法相关联时,它首先会查阅该表。


3

C# 和 VB.NET 之间并没有根本性的差异,它们只是选择了不同的方式来解决歧义。最好用以下 C# 代码片段来展示:

interface ICowboy {
    void Draw();
}
interface IPainter {
    void Draw();
}

class CowboyPainter : ICowboy, IPainter {
    void ICowboy.Draw() { useGun(); }
    void IPainter.Draw() { useBrush(); }
    // etc...
}

VB.NET选择了一致的接口实现语法,这样程序员就不必权衡隐式和显式实现语法之间的差异。在VB.NET中始终使用显式实现即可。

只有接口方法的可访问性才是重要的。始终使用public。


除了在 VB 中,访问修饰符只有在 通过接口类型的对象调用时才相关。VB 实际上创建了两种方法——一种可通过接口调用(其中访问修饰符无关紧要),另一种可通过其他方式调用(其中访问修饰符相关)。 - Dave Doknjas
@DaveDoknjas:如果Foo实现了IFoo,并且有一个方法void Bar(),那么定义public void Foo()等同于Public Sub Foo() Implements IFoo.Foo,而void IFoo.Foo()等同于Private Sub xyzzy24601_IFoo_Foo_() Implements IFoo.Foo。这是C#允许方法实现接口的唯二模式。VB.NET也允许其他模式,例如protected void ProtFoo() Implements IFoo.Foo,而在C#中,相当的功能需要使用两个方法:protected void ProtFoo() {...} void IFoo.Foo { ProtFoo(); } - supercat
@supercat:'private'示例需要与您的'protected'示例相同的逻辑 - 您需要允许'this'访问类内的方法,而不通过接口来重现VB的'Private'情况。 - Dave Doknjas
@DaveDoknjas:在VB.NET中,如果声明方法为“Private”,则可以使用指定的任何名称在类内部使用该成员。在C#中,编译器不允许程序员指定名称,而是随意发明一个名称,这个名称可能会在编译之间变化。显式实现接口的方法将在私有范围内生成标识符,但是程序员不知道该名称是什么,除非检查生成的代码和/或使用反射来引用它。 - supercat
@supercat:我想要表达的是,“void IFoo.Foo()”本身并不足以提供与“Private Sub Foo() Implements IFoo.Foo”等效的功能。你还需要“private void Foo()”才能在类内部访问而无需接口实例。 - Dave Doknjas

2
当您将变量stdnt声明为IStudent时,接口方法和属性就会变为公共的,因此派生类(Student)的实现会被执行。另一方面,如果stdnt被声明为Student,则私有成员(Name和SayHello)不会被实现,并且会抛出错误。
我猜测接口成员存根(Name和SayHello)默认为public,而派生类实现的访问修饰符定义则被忽略。
以上是我的个人看法。

1
C# 中的确切等效代码如下 - 接口类型对象可用方法和其他情况下可用的私有方法:
   void IStudent.SayHello()
   {
       this.SayHello();
   }
   private void SayHello()
   {
       Console.WriteLine("Say Hello!");
   }

看一下我发布的代码 - 这相当于 VB 中访问修饰符为“Private”的情况 - 该方法仍可通过接口实例调用,但只能在类内部调用。 - Dave Doknjas
2
换句话说,在C#和VB中,该方法始终可以通过接口实例访问。在VB中,访问修饰符仅适用于未通过接口实例调用该方法的情况 - 你可以称之为VB的糟糕语言设计。 - Dave Doknjas

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