Class design style rules

Code review rules: Problem list

The class design rules form a group of style issues related to class definitions, hierarchy and class members. This group of code review rules, part of the problem detection feature, detects potential problems with your classes, structures and interfaces. As VB.NET is a more object-oriented language than earlier versions, most of the rules are designed for .NET classes. There are also some rules for VB Classic classes (in VB versions 4.0 to 6.0).

As these rules are related to programming style, not all people will agree on which style is the best. Pick the rules that suit your use.

VB.NET constructors (Sub New)

The following rules apply to defining the correct constructors for .NET classes. See the appropriate rule for each class type.

We use the following terminology: Shared class = class that has only Shared members. Abstract class = MustInherit class. Concrete class = any other class.

Constructor missing. A concrete .NET class should define at least one constructor for instantiating objects of the class. The scope of the constructor should be used to define who can instantiate the objects. A Shared constructor is not enough for a concrete class. This rule skips abstract MustInherit classes and classes that have only Shared members. These special class types are subject to the rules Protected constructor expected and Private constructor expected, respectively. CTOR

Protected constructor expected. Constructors of an abstract MustInherit class in .NET can only be called from an instantiating subclass. Marking all constructors Protected helps indicate this. Having no constructors in an abstract class is also a viable alternative. This rule ignores Shared constructors. CTOR

Private constructor expected. A .NET class that has only Shared members should define a private constructor to prevent instantiation. There is no value in creating an instance of a class that contains only Shared members. To prevent such extraneous instantiation, ensure that the class has a single, no-argument, private constructor and no other constructors. This rule applies to .NET classes having only Shared procedures and variables. Sub New and Finalize are not taken as procedures. CTOR

VB.NET Destructors (Sub Finalize)

The following set of rules applies to finalization of .NET classes. The destructor Sub Finalize executes when a object has been disposed of and the .NET run-time garbage collector (GC) is about to free the object. You can use Project Analyzer to set requirements for your destructor strategy. The alternatives are the following:

Finalize found. A .NET class defines a Finalize destructor. Finalization is generally not required if the class uses only .NET managed resources. The existence of a Finalize destructor will slow down the garbage collection process, which has a negative performance impact if a large number of instances of a class are created and destroyed. For maximum speed, finalization should be left to classes that allocate unmanaged resources. FINALIZE

IDisposable not implemented. A .NET class defines a Finalize destructor but does not implement the IDisposable interface. An object that allocates some resources should implement IDisposable.Dispose to free the resources when the object is no more required. Users can call Dispose to free the resources as soon as possible. Relying on the Finalize destructor to release resources will lead to resources staying allocated for a longer time than what is necessary. This happens because the garbage collection process will execute Finalize asynchronously at a later time. Read .NET help for 'Implementing a Dispose method' for information on how to implement finalization with IDisposable. See also the rule SuppressFinalize missing. NO_IDISPOSABLE

Finalize missing. A .NET class defines no Finalize destructor. Proper finalization is required if the class uses unmanaged resources. Finalization is generally not required if the class uses only .NET managed resources. The compulsory use of a Finalize method is a matter of programming style. NO_FINALIZE

Finalize missing MyBase.Finalize. A Finalize destructor of a .NET class should call MyBase.Finalize to free up any resources allocated by the base class. If the class derives directly from System.Object, this call is not necessary. NO_MYBASE_FINALIZE

SuppressFinalize missing. A method implementing IDispose.Dispose should call GC.SuppressFinalize(Me). The Dispose method should make this call to prevent the Finalize destructor from getting executed by the garbage collection process. If the Dispose method frees all allocated resources, it is unnecessary for the GC to call Finalize to free them again. By calling GC.SuppressFinalize(Me) you make the garbage collection run more efficiently. This rule applies to .NET classes implementing the IDispose interface and having a Finalize destructor. NO_SUPPRESSFINALIZE

VB.NET class hierarchy

Parent class instantiated. An instance of a .NET parent (base) class is created. Keeping parent classes abstract and instantiating only leaf classes will make your class inheritance tree more clear. See also the rule Parent class requires MustInherit. PARENT_INST

Parent class requires MustInherit. Add MustInherit to all your .NET parent (base) classes to prevent accidental instantiation. Keeping parent classes abstract and instantiating only leaf classes will make your class inheritance tree more clear. See also the rule Parent class instantiated. NO_MUSTINHERIT

Class looks like Interface. An abstract .NET class looks like an Interface in that it defines actions but does not implement them. Could you rewrite the class as an Interface to make this explicit? If the only purpose of the class is to define a set of procedures for descendants to implement, redefining it as an Interface might be more appropriate. It is a matter of programming style which way you prefer. This rule finds abstract MustInherit classes that define one or more empty procedures. In addition, they must not define any implementation, that is, variables, Private procedures or any procedures with code in them. If the class inherits another class (other than System.Object), the parent must also look like Interface (except that it does not necessarily have to define any procedures). LIKE_INTERFACE

Inheritance limited. All methods and properties of a class are sealed but the class is not. The class can be derived but none of the derived methods or properties can be overridden by subclasses. Subclasses can thus add new functionality but they cannot modify existing behavior. This restriction can be considered a form of limited inheritance, indicating a possible design flaw. You have two choices to clear things up. If the class is not designed to be derived from, prohibit inheritance by adding a NotInheritable keyword to the class and removing any NotOverridable keywords from the methods. This way you explicitly seal the class to disallow limited inheritance. Alternatively, go from limited inheritance to more versatile inheritance by defining some appropriate methods Overridable. You can also choose to overlook this problem if limited inheritance is what you want to achieve. INHERITANCE_LIMITED

VB.NET class members

Shadows keyword found. The Shadows keyword in VB.NET allows a child class to hide members of its ancestor classes. Shadowing is not recommended as it makes programs harder to understand. It may not be clear to a developer that there are two different things with the same name. See also the rule Child reuses ancestor member name. SHADOWS

Child reuses ancestor member name. A child class member has the same name as a private member of its ancestor class. Even though a child class is allowed to have any member names, it should not reuse the names of private members of its ancestors. The use of similar names increases the probability of programming errors. First, it may not be clear to a developer that there are two different things with the same name. Second, if the ancestor member scope is later changed, it will result in name shadowing. This rule is related to the Shadows keyword but technically speaking, it is not actual name shadowing because a private ancestor member is not visible to the child. REUSES_NAME

Member scope exceeds container scope. The scope of a member is wider than the scope of the class, module or structure that it belongs to. A member cannot have higher visibility than its container. Although this causes no immediate faults, it may make the code harder to understand and more prone to errors. If the scope of the container is changed later, the members may accidentally have higher visibility than desired. Notice that the scope of the container may be set automatically by VB (if no scope is defined) or it may be further limited by the container of the container. This problem is shown if the member explicitly defines too wide a scope. It is not shown if the member has implicit scope declaration, that is, no scope keyword. As an exception, if a procedure is declared with the Overrides keyword, its scope cannot be defined freely and is thus not subject to this rule. Member scope checking is available for VB.NET only. In VB Classic, scope choices are limited and not likely to cause confusion. MEMBER_SCOPE

Protected found in NotInheritable class. The use of a Protected or Protected Friend scope for a member of a NotInheritable class makes no sense as there cannot be any subclasses. Rewrite Protected as Private and Protected Friend as Friend. As an exception, Protected and Protected Friend may be required in an Overrides declaration. PROTECTED

Miscellaneous (VB4 and later)

Interface members missing. A class, .NET Structure or .NET Interface does not define any members to interface with other parts of the code. You cannot access the contents of the class/structure/interface because there is no way to execute methods or pass data. This could indicate a design flaw, some code is possibly missing. The following definitions count as interface members for this rule: non-private sub/function/property, non-private variable. Additionally, you may use the Implements keyword to access members of the class via another interface, provided that the other interface is not empty too. Inherited members do not count as interface members, nor do DLL procedure declarations. This rule works for both VB.NET and classic VB classes. NO_INTERFACE

Interface class instantiated. A VB5/VB6 .cls file defines an interface that is being implemented by another class, yet the class is also instantiated. Keeping interface classes and concrete classes separate will help in clarifying the program's structure. An interface class should be pure in that it contains no executable code and that it is not instantiated. See also the rule Interface class contains code. INTERFACE_INST

Interface class contains code. A VB5/VB6 .cls file defines an interface and also contains executable code. The class's interface is being implemented by another class, yet there is code in the class's procedures. It is a mix of interface definition and concrete code. Keeping interface classes and concrete classes separate will help in clarifying the program's structure. An interface class should be pure in that it contains no executable code and that it is not instantiated. See also the rule Interface class instantiated. INTERFACE_WITH_CODE

Comment directive

The rules on this page are covered by the comment directive parameter STYLE.

See also

Problem detection
Code review rules: Problem list

©Aivosto Oy - Project Analyzer Help Contents