[Translation] Type Design Principles

Source: Internet
Author: User
Tags protected constructor

This article is translated by cyjb from Type Design Guidelines (. NET Framework 4.5 ).

For CLR, there are only two types: reference type and value type. However, in order to discuss the framework design, we divide the types into more logical groups, with each group having its specific design principles.

A class is a common reference type. Most types in the framework are classes. Class is very popular because it supports most of the object-oriented features and general adaptability. The base class and abstract class are special logical groups related to scalability.

An interface is a type that can be implemented by the reference type and value type. They can be used as the root of the hierarchical structure of the reference type and value type, or simulate multi-inheritance (CLR itself does not support multi-inheritance ).

Struct is a common value type used to represent small simple types, similar to the basic types of languages.

Enumeration is a special value type used to define a set of values, such as weeks and console colors.

A static class is a type designed to contain static members. It is usually used to provide shortcuts for other operations.

Delegation, exceptions, features, arrays, and collections are all special reference types for specific purposes. Their design and usage principles will be discussed elsewhere in this book.

√ YesMake sure that each type is a well-defined set of related members, not just a random set of irrelevant functions.

Select a class or struct

One of the basic decisions that every framework designer will face is to design a type as a class (reference type) or a struct (value type ), therefore, it is necessary to understand the behavioral differences between the reference type and the value type.

The first difference between the reference type and the value type is that the reference type is allocated to the heap and will be recycled. The value type is allocated to the stack, or is joined to the include type and recycled when the stack is expanded or the include type is released. Therefore, the allocation and collection of value types are generally less costly than those of reference types.

Second, arrays of the reference type are not continuously allocated, that is, the array elements are only references to the reference type instance located on the stack. The array of value type is continuously allocated, which means that the array element is actually a value type instance. Therefore, the allocation and collection of value-type arrays are less overhead than those of reference-type arrays. In addition, in most cases, value-type arrays show better locality than reference-type arrays.

Then there is the difference with memory usage. The value type is boxed when it is converted to a reference type or implemented interface, and is split when it is converted back to a value type. Because the packing result is the objects allocated to the stack, they will be recycled. Too many packing and unpacking will negatively affect the heap and garbage collector, and ultimately affect the application performance. In contrast, the reference type will not be boxed when converted.

Next, the reference is copied when the reference type is assigned, and the complete value is copied when the value type is assigned. Therefore, the assignment of a large reference type is less costly than that of a large value type.

Finally, the reference type is passed by reference, and the value type is passed by value. Changes to the instance type will affect all references pointing to the instance. A value-type instance will be copied when it is passed by value. When the value-type instance is changed, it will not obviously affect its other copies. Because a copy of the value type is not explicitly created by the user, but is implicitly created when parameters are passed or the return value is returned, variable value types may confuse many users, the value type should be unchangeable.

Based on experience, most types of frameworks should be classes. However, in some cases, the value type makes it more suitable to use struct.

√ ConsiderationsDefines struct rather than classes. If the type of instance is small and usually has a short life cycle or is embedded in other objects.

X notDefine struct unless it has all of the following features:

  • It logically represents a single value, similar to the primitive type (INT, double, etc.
  • The instance size is smaller than 16 bytes.
  • It is immutable.
  • It does not need to be packed frequently.

In all other cases, you should define your type as a class.

Abstract class design

X notDefine the public or protected internal constructor in the abstract class.

Public constructor is required only when you need to create an instance of the type. Because you cannot create abstract instances, abstract types with common constructor are incorrect designs and mislead users.

√ YesDefine the protected or internal constructor for the abstract class.

A protected constructor is more common and allows the base class to complete its own initialization when the subclass is created.

An internal constructor can be used to restrict the specific implementation of an abstract class in a set of programs defining this class.

√ YesEach abstract class that you provide provides at least one specific inheritance type.

This helps verify the design of abstract classes. For example, system. Io. filestream is an implementation of the system. Io. Stream abstract class.

Static class design

A static class is a class that only contains static members (except for the instance members inherited from system. Object and possible private constructor ). Some languages provide built-in static class support. In C #2.0 and later versions, when a class is defined as static, it is sealed, abstract, and has no instance members that can be declared or overwritten.

Static classes compromise between pure object-oriented design and conciseness. They are generally used to provide shortcuts for other operations (such as system. io. file), storage extension method or not suitable for using fully object-oriented packaging functions (such as system. environment ).

√ YesExercise caution when using static classes.

Static classes should only be used to support the object-oriented core classes in the framework.

X notI think static classes can be all-encompassing.

X notDeclare or override instance members in a static class.

√ YesDeclare static classes as sealed, abstract, and add a private member constructor if your programming language does not support built-in static classes.

Interface Design

Although most APIs are most suitable for class and struct modeling, in some cases, interfaces are more appropriate or only available.

CLR does not support multi-inheritance (that is, the CLR class cannot inherit from more than one base class), but it allows the type to implement one or more interfaces beyond one base class. Therefore, interfaces are often used to implement multi-inheritance. For example, idisposable is an interface that allows types to release resources. It is independent of any other inheritance hierarchy.

Another scenario that is suitable for defining interfaces is to create public interfaces that can be supported by multiple types (including value types. Value types cannot inherit from other types other than valuetype, but they can implement interfaces. Therefore, using interfaces becomes the only option to provide common basic types.

√ YesDefine interfaces. If you need public APIs that are supported by multiple types that contain value types.

√ ConsiderationsDefine an interface. If you support the function of a type that has been inherited from another type.

X avoidUse the tag interface (an interface without any members ).

If you need to mark a class as a special feature (TAG), you generally use a custom attribute instead of an interface.

√ YesProvides at least one implementation type for the interface.

This will help verify the implementation of the interface. For example, list <t> is an implementation of the ilist <t> interface.

√ YesEach interface you define provides at least one API that uses it (the method that uses the interface as a parameter or the attribute of the interface type ).

This will facilitate the design of the verification interface. For example, list <t>. Sort uses the system. Collections. Generic. icomparer <t> interface.

X notAdd a member to a public interface.

This may damage the existing implementation of the interface. You should create a new interface to avoid version problems.

When designing reusable hosted code libraries, except for the situations described above, you should generally choose to use classes rather than interfaces.

Structure Design

The common value type is often called struct (struct), a C # keyword. This section provides general structure design principles.

X notProvides Default constructors for struct.

This rule allows you to create struct arrays without calling constructors for each array element. Note that C # does not allow Default constructors for struct.

X notDefines a variable struct.

Variable struct has some problems. For example, when the get accessor of the attribute returns a value type, the caller will get a copy of the returned value. Because the replicas are created implicitly, developers may not realize that they are modifying the replicas, rather than the original values. In addition, some languages (especially dynamic languages) have problems when using variable value types, because even local variables produce copies when the reference is canceled.

√ YesMake sure that all instance data is set0,falseOrnullWhen applicable, the status is valid.

This is to prevent unexpected creation of invalid instances when creating struct arrays.

√ YesImplement iequatable <t> On the value type.

The value-type object. Equals method causes the packing operation, and its default implementation is not very efficient because reflection is used. Implementing the iequatable <t>. Equals method can have higher performance and will not cause packing.

X notShow extended valuetype. In fact, most languages will block this.

Normally, struct can be very useful, but should only be used for small values (smaller than 16 bytes, as described above), single, immutable and not frequently boxed values.

Enumeration Design

Enumeration is a special value type, which can be classified into simple enumeration and flag enumeration.

Simple enumeration indicates a small closed set. A common simple enumeration example is a set of colors.

Flag enumeration is designed to support bitwise operation of enumeration values. An example of a common flag enumeration is an option list.

√ YesThe parameters, attributes, and return values of the set of values are strongly typed by enumeration.

√ YesEnumeration instead of static constants are preferred.

X notUse enumeration for Open Sets (such as the operating system version and your friend's name.

X notProvides future reserved enumerated values.

You can always simply add values for existing enumeration later. For more information, see add values to enumeration. The reserved value only pollutes the actual value and often leads to user errors.

X avoidPublic exposure only contains one enumerated value.

A common way to ensure the future scalability of c api is to add retention parameters for method signatures. Such reserved parameters can be enumerated with a single default value. This should not be done in managed APIs. Method overloading allows adding parameters in future versions.

X notContains the Sentinel value in the enumeration.

Although the sentry value can sometimes help framework developers, it can confuse users of the framework. They are used to track the enumeration status, rather than a value in the set represented by enumeration.

√ YesProvide a zero value in simple enumeration.

Name zero as "NONE ". If "NONE" is not suitable for this enumeration, the basic value 0 should be assigned to the most common default value of enumeration.

√ ConsiderationsInt32 (the default data type in most programming languages) is used as the basic type of enumeration, unless any of the following situations occurs:

  • Enumeration is a flag enumeration, And you have more than 32 logos, or you want to have more logos in the future.
  • The basic type must be different from int32 to facilitate interoperability with the unmanaged code that expects enumeration of different sizes.
  • Small basic types can significantly save space. If you want enumeration to be primarily used as a control flow parameter, its size is not very important. Space saving may be important in the following situations:
    • You expect enumeration to be used as fields in struct or classes that are frequently instantiated.
    • You are expected to create a large array or set of enumeration instances.
    • You are expected to serialize a large number of enumeration instances.

For enumeration in the memory, note that the hosted object is always aligned by DWORD. Therefore, you 'd better fill the instance with multiple enumerations or small structs, because the total instance size is always rounded up to DWORD ).

√ YesThe names of nouns and phrases are used for enumeration. For simple enumeration, singular nouns or noun phrases are used.

X notDirectly expand system. enum.

System. Enum is a special type used by CLR to create user-defined enumeration. Most programming languages provide language elements for you to use this feature. For example, The enum Association word in C # is used to define enumeration.

Design tag Enumeration

√ YesTo enumerate the application system. flagsattribute. Do not apply this feature to simple enumeration.

√ YesUse the power of 2 for the enumerated values so that these values can be freely combined using bitwise OR.

√ ConsiderationsProvides special enumerated values for common logo combinations.

Bitwise operations are an advanced concept and are not necessary for simple tasks. Readwrite is an example of this special value.

X avoidCreate a flag enumeration when some combination values are invalid.

X avoidUse a zero flag for enumeration, unless such a value indicates "all flag are cleared", and the name is correct as described in the next criterion.

√ YesName the zero value of the Flag enumeration as "NONE ". For flag enumeration, this value must always indicate "all flags are cleared ".

Add value to Enumeration

When you have published an enumeration, you will often find that you need to add more values to it. When a newly added value is returned by an existing API, program compatibility issues may occur because poorly written applications may not be able to properly process the new value.

√ ConsiderationsAdd a value to the enumeration, even if there is a small compatibility risk.

If you are sure that adding a value to the enumeration will cause program compatibility problems, consider adding a new API that returns the new value and the old value, and adding the old API (still only return the old value) mark as obsolete. This ensures that your existing programs are compatible.

Nested type

The nested type is the type defined in the range of another type (the so-called closed type). This is the so-called closed type. A nested type has the right to access all members of its closed type. For example, it has the right to access private fields defined in the closed type and protected fields defined in all the ancestors of the closed type.

The nested type should be used with caution for several reasons. Some developers are not completely familiar with the concept of nested types. For example, these developers may not understand the syntax for declaring nested type variables. The nested type is closely related to its closed type, so it is not suitable to use it as a common type.

Nested types are most suitable for constructing the implementation details of their closed types. End users should rarely declare nested type variables, and almost never need to explicitly instantiate nested types. For example, the iterator of a set can be the nested type of the set. Iterators are usually instantiated by their closed types, and because many languages Support foreach statements, iterator variables rarely need to be declared by the end user.

√ YesThe nested type is used. When the relationship between the nested type and its external type requires the accessible semantics of the member.

X notUse the common nesting type as the logical grouping structure. Use the namespace.

X avoidExposes the nesting type publicly. The only special case is that variables of the nested type need to be declared, for example, in rare cases such as subclass or other advanced customization.

X notUse a nested type. If the type is included, it may be referenced externally.

For example, the enumeration passed to a class-defined method should not be defined as the nested type of the class.

X notUse the nested type. If you need to instantiate the type by the client code. If a type has a common constructor, it is better not to be nested.

If a type can be instantiated, it is generally considered that the type can be used independently in the framework (you can create it, use it, and destroy it without using an external type ), therefore, it should not be nested. In the absence of any relationship with external types, internal types should not be extensively reused outside the external type.

X notDefine the nested type as a member of the interface. Many languages do not support this structure.

Portions©2005,200 9 Microsoft Corporation. All rights reserved.

Reprinted by permission of Pearson Education, Inc. from Framework Design Guidelines: Conventions, idioms, and patterns for reusable. net libraries, 2nd edition by Krzysztof cwalina and Brad Abrams, published Oct 22,200 8 by Addison-Wesley professional as part of the Microsoft Windows development series.

[Translation] Type Design Principles

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.