模块真的和SharedMembers-NotInheritable-PrivateNew类完全相同吗?

时间:2019-02-14 13:09:14

标签: vb.net

我可以从Internet上了解到很多知识,即VB.Net模块与c#.Net静态类是同一件事。我还可以阅读到类似于Static Class的东西是一个看起来像这样的类:

'NotInheritable so that no other class can be derived from it
Public NotInheritable Class MyAlmostStaticClass

    'Private Creator so that it cannot be instantiated
    Private Sub New()
    End Sub

    'Shared Members
    Public Shared Function MyStaticFunction() as String
        Return "Something"
    End Function

End Class

我发现这段代码很容易起草和阅读。像这样使用Module,我会感到更加自在:

Public Module MyEquivalentStaticClass
    Public Function MyStaticFunction() as String
        Return "Something"
    End Function
End Module

但是,对于Module,您将失去Namespace层次结构的一个级别,并且以下3条语句相等:

'Call through the Class Name is compulsory
Dim MyVar as String = Global.MyProject.MyAlmostStaticClass.MyStaticFunction()

'Call through the Module Name is OPTIONNAL
Dim MyVar as String = Global.MyProject.MyEquivalentStaticClass.MyStaticFunction()
Dim MyVar as String = Global.MyProject.MyStaticFunction()

我觉得这很不方便,这会污染Intelisense,或者迫使我创建Namespace的附加级别,这意味着需要更多Module声明,即更多Intelisense污染。

如果您要避免使用繁重的SharedMembers-NotInheritable-PrivateNew Class声明,是否有解决方法?或者要为此付出代价吗?

其他参考文献包括Cody Gray的精彩文章:https://stackoverflow.com/a/39256196/10794555

3 个答案:

答案 0 :(得分:7)

不,在VB.NET中没有与C#静态类完全相同的东西。如果VB能够将Shared修饰符添加到类声明中,就像这样:

Public Shared Class Test  ' This won't work, so don't try it
    ' Compiler only allows shared members in here
End Class

但是,不幸的是,事实并非如此。如果这样做,编译器会给您以下错误:

  

不能将类声明为“共享”

这为我们提供了您列出的两个选项:

  • 您要创建一个仅包含Shared个成员的非实例化类(在编译器不强制执行该规则的情况下),或者
  • 使用Module,它可以使所有内容Shared,即使您没有通过Shared修饰符明确地这么说

正如您所说,很多人不喜欢丢失类名,因为它是一种额外的命名空间层,因此他们更喜欢Class仅具有Shared个成员的情况。 Module。但是,这是一个优先事项。

值得注意的是,虽然您不必 在调用其成员的任何地方指定模块名称,但是您可以随时这样做:

MyModule.MyMethod()

尽管您雄辩地称其为“ SharedMembers-NotInheritable-PrivateNew类”,但它与静态类最接近,但在功能上却是等效的。如果使用反射,则会看到类型的属性不相同。例如,在VB中:

Module MyModule
    Public Sub Main()
        Dim t As Type = GetType(MyClass)
    End Sub
End Module

Public NotInheritable Class MyClass
    Private Sub New()
    End Sub

    Public Shared Sub MyMethod()
    End Sub
End Class

如果您查看t.Attributes,就会发现它等于Public Or Sealed。因此MyClass类型既是密封的(NotInheritable)又是公共的。但是,如果您在C#中执行此操作:

class Program
{
    static void Main(string[] args)
    {
        Type t = typeof(Test);
    }
}

public static class MyClass
{
    public static void MyMethod()
    { }
}

然后您再次检查t.Attributes,这次的值为Public | Abstract | Sealed | BeforeFieldInit。不一样由于您无法同时在VB中将一个类声明为NotInheritableMustInherit,因此您没有机会完全复制该内容。因此,尽管它们或多或少是等效的,但类型的属性却不同。现在,只是为了好玩,让我们尝试一下:

Module MyModule
    Public Sub Main()
        Dim t As Type = GetType(MyModule)
    End Sub
End Module

现在,模块的t.AttributesSealed。而已。仅Sealed。所以也不一样。在VB中获得真正的静态类的唯一方法(意味着,通过反射检查类型具有相同的属性)是将其写入C#类库,然后在VB中引用该库。

答案 1 :(得分:0)

  

仅使用模块,我会感到更加舒适

因此,请使用模块。

Module SomeModuleNameHere

    Public Function MyStaticFunction() As String
        Return "Something"
    End Function

End Module

您根本不需要Global.MyProject或模块名称,只需在任何地方直接调用函数即可:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim x As String = MyStaticFunction()
    Debug.Print(x)
End Sub

但是,如果需要,您可以使用模块名称,而无需使用Global部分:

Dim x As String = SomeModuleNameHere.MyStaticFunctions

但是,唯一必须使用模块名称的时间是,如果在不同模块中有两个具有完全相同名称的函数。然后,您必须使用它们的完全限定名称来区分它们。

答案 2 :(得分:0)

在到目前为止进行的所有讨论中,多亏了史蒂芬·多格特(Steven Doggart)的投入和TnTinMn的评论,我得出了以下广泛的反馈意见和指南。

  • Nota:这篇文章引用了“静态”类,而Static关键字用于C#.Net,而不是VB.Net。 VB等效为Shared,但VB不允许使用Shared类(仅成员)。下面介绍的准则是在VB中接近C#Static Class的尝试。 由于此类VB类不能为Shared,因此将其描述为“静态”。
  • Nota bis:在所有示例中,我有意添加了一层Namespace(始终称为“ MySpace”),以使这些示例位于Namespace层中不会造成混淆:都在MySpace层中。 MySpace层不是强制性的,可以根据需要将其剥离。

一般

使用Module,但不要依赖Module名称作为Namespace层。而是将路径完全集成到Namespace声明中,例如:

Namespace MySpace.MyStaticClass
    Module _Module

        Function MyStaticFunction()
            Return "Something"
        End Function

    End Module
End Namespace

然后应通过Global.MyProject.MySpace.MyStaticClass.MyStaticFunction()

访问静态“成员”
  • 注意:Namespace路径的一部分可以剥离,具体取决于位置 您位于。通常,MySpace.MyStaticClass.MyStaticFunction() 就足够了。
  • 通知之二:使用_Module作为Module名称将 降低Intelisense下拉菜单中模块的外观,以及 还要明确地说这是一个模块。

当希望包含静态类时

在这种情况下,上面提到的一般样式将产生:

Namespace MySpace.MyStaticClass
    Module _Module

        Function MyStaticFunction()
            Return "Something"
        End Function

    End Module
End Namespace

Namespace MySpace.MyStaticClass.MyStaticSubClass1
    Module _Module

        Function MyStaticFunction()
            Return "Something"
        End Function

    End Module
End Namespace

Namespace MySpace.MyStaticClass.MyStaticSubClass2
    Module _Module

        Function MyStaticFunction()
            Return "Something"
        End Function

    End Module
End Namespace

在每个“封装的”“静态类”需要一个单独的命名空间声明的意义上,这可能很快变得很沉重。缺点包括:

  1. 进行大量检查,因为了解Namespace体系结构/树状结构将不太直观:在上述示例中,这意味着检查所有包含“ MyStaticClass”的声明。
  2. 由于附加的Namespace声明,因此起草繁重。
  3. 繁重的维护工作,因为更改父项Namespace将需要对多个Namespace声明进行更改:在上面的示例中,这意味着将'MyStaticClass'更改3次。 (右键单击/重命名是您最好的朋友)

一种替代方法是使用具有共享成员的封装类:

Namespace MySpace
    Public Class MyStaticClass

        Public Function MyStaticFunction()
            Return "Something"
        End Function

        Public Class MyStaticSubClass1

            Public Shared Function MyStaticFunction()
                Return "Something"
            End Function

        End Class

        Public Class MyStaticSubClass2

            Public Shared Function MyStaticFunction()
                Return "Something"
            End Function

        End Class

    End Class
End Namespace
  • Nota:正如Steven Doggart在separate post中指出的那样,人们通常使用import命名空间,但不使用import类,因此封装类通常会“强制”完全依赖封装的类之间的路径:MyStaticClass.MyStaticSubClass1

您不能将Module封装在另一个Module中,但始终可以使用Module的混合物来封装一个或多个类和子类。下面的示例实现了与上面的示例类似的功能:

Namespace MyStaticClass
    Public Module _Module

        Public Function MyStaticFunction()
            Return "Something"
        End Function

        Public Class MyStaticSubClass1

            Public Shared Function MyStaticFunction()
                Return "Something"
            End Function

        End Class

        Public Class MyStaticSubClass2

            Public Shared Function MyStaticFunction()
                Return "Something"
            End Function

        End Class

    End Module
End Namespace

发布类库(DLL)时

如果最终产品是DLL,并且打算与更多的读者共享,则建议在“静态”类别周围放置安全网。尽管这不会影响编译器查看代码的方式,但可以防止其他人犯错误,或者至少可以快速触发错误并迅速进行调试:

  1. 制作Class NotInheritable,以便没有人尝试从静态Class派生Class:通常派生这样的类是没用的。
  2. 使用New创建者语句Private,以便没有人尝试实例化Class:静态Class不应包含任何非静态({{1 }})成员;如果是这样,那是一个错字,尝试实例化非共享成员可能会带来问题。

下面的示例实现了与上面的示例类似的功能:

Shared

在处理扩展程序时

只能在Namespace MySpace Public NotInheritable Class MyStaticClass Private Sub New() End Sub Public Function MyStaticFunction() Return "Something" End Function Public NotInheritable Class MyStaticSubClass1 Private Sub New() End Sub Public Shared Function MyStaticFunction() Return "Something" End Function End Class Public NotInheritable Class MyStaticSubClass2 Private Sub New() End Sub Public Shared Function MyStaticFunction() Return "Something" End Function End Class End Class End Namespace 块中声明<System.Runtime.CompilerServices.Extension()>。但是Module名称对扩展名没有影响,因此,此主题在这里并没有实际意义。

请参阅Peter Macej提供的链接:https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/procedures/extension-methods