All .NET Framework operations indicate failure by throwing exceptions. Although you can recover from most application exceptions, you cannot recover from most runtime exceptions.
The runtime creates an exception information table for each executable. Exception table is extremely efficient and does not cause any performance penalty in processor time or in memory use when an exception does not occur. You use resources only when an exception occurs.
If the debugger does not attach to the exception, the runtime raises the AppDomain.UnhandledException event. If there are no listeners for this event, the runtime dumps a stack trace and ends the application.
There are two types of exceptions: exceptions generated by an executing program, and exceptions generated by the common language runtime.
It is not recommended that you catch a SystemException, nor is it good programming practice to throw a SystemException in your application.
It is good coding practice to add information to an exception that is re-thrown to provide more information when debugging.
When creating your own exceptions, it is good coding practice to end the class name of the user-defined exception with the word “Exception.” It is also good practice to implement the three recommended common constructors, as shown in the following example.
Some resource cleanup, such as closing a file, must always be executed even if an exception is thrown. To accomplish this, you can use a finally block. A finally block is always executed, regardless of whether an exception is thrown.
In catch blocks, always order exceptions from the most specific to the least specific. This technique handles the specific exception before it is passed to a more general catch block.
Throw exceptions instead of returning an error code or HRESULT.
Return null for extremely common error cases instead of throwing an exception. In other words do not return null, throw exception. An extremely common error case can be considered normal flow of control. By returning null in these cases, you minimize the performance impact to an app.
In most cases, use the predefined .NET Framework exception types. Introduce a new exception class only when a predefined one doesn’t apply.
Throw an InvalidOperationException exception if a property set or method call is not appropriate given the object’s current state.
Throw an ArgumentException exception or a class derived from ArgumentException if invalid parameters are passed.
For most apps, derive custom exceptions from the Exception class.
Deriving from the ApplicationException class doesn’t add significant value.
Use grammatically correct error messages, including ending punctuation. Each sentence in a description string of an exception should end in a period. For example, “The log table has overflowed.” would be an appropriate description string.
This example demonstrates how to use I/O classes to synchronously copy the contents of a directory to another location. In this example, the user can specify whether to also copy the subdirectories. If the subdirectories are copied, the method in this example recursively copies them by calling itself on each subsequent subdirectory until there are no more to copy.
Scenarios for Isolated Storage
Isolated storage is useful in many situations, including these four scenarios:
- Downloaded controls. Managed code controls downloaded from the Internet are not allowed to write to the hard drive through normal I/O classes, but they can use isolated storage to persist users’ settings and application states.
- Shared component storage. Components that are shared between applications can use isolated storage to provide controlled access to data stores.
- Server storage. Server applications can use isolated storage to provide individual stores for a large number of users making requests to the application. Because isolated storage is always segregated by user, the server must impersonate the user making the request. In this case, data is isolated based on the identity of the principal, which is the same identity the application uses to distinguish between its users.
- Roaming. Applications can also use isolated storage with roaming user profiles. This allows a user’s isolated stores to roam with the profile.
You should not use isolated storage in the following situations:
- To store high-value secrets, such as unencrypted keys or passwords, because isolated storage is not protected from highly trusted code, from unmanaged code, or from trusted users of the computer.
- To store code.
- To store configuration and deployment settings, which administrators control. (User preferences are not considered to be configuration settings because administrators do not control them.)
Isolated storage location
Anonymous pipes provide interprocess communication on a local computer. They offer less functionality than named pipes, but also require less overhead. You can use anonymous pipes to make interprocess communication on a local computer easier. You cannot use anonymous pipes for communication over a network.
Named pipes provide interprocess communication between a pipe server and one or more pipe clients. They offer more functionality than anonymous pipes, which provide interprocess communication on a local computer. Named pipes support full duplex communication over a network and multiple server instances, message-based communication, and client impersonation, which enables connecting processes to use their own set of permissions on remote servers.
Dates, Times, and Time Zones
Use TimeZoneInfo class to work with any time zone that is predefined on a system, to create new time zones, and to easily convert dates and times from one time zone to another. For new development, use the TimeZoneInfo class instead of the TimeZone class.
In some cases, taking full advantage of the TimeZoneInfo class may require further development work. First, date and time values are not tightly coupled with the time zones to which they belong. As a result, unless your application provides some mechanism for linking a date and time with its associated time zone, it is easy for a particular date and time value to become disassociated from its time zone. (One method of linking this information is to define a class or structure that contains both the date and time value and its associated time zone object.) Second, Windows XP and earlier versions of Windows have no real support for historical time zone information, and Windows Vista has only limited support. Applications that are designed to handle historical dates and times must make extensive use of custom time zones.
The TimeZoneInfo class does not expose a public constructor. As a result, the new keyword cannot be used to create a new TimeZoneInfo object. Instead, TimeZoneInfo objects are instantiated either by retrieving information on predefined time zones from the registry or by creating a custom time zone.
For time zones that are not defined in the registry, you can create custom time zones by calling the overloads of the CreateCustomTimeZone method.
The best way to make sure that you supply a valid identifier is to enumerate the time zones available on your system and note their associated identifiers.
Call the TimeZoneInfo.GetSystemTimeZones method. The method returns a generic ReadOnlyCollection<T> collection of TimeZoneInfo objects.
An ambiguous time is a time that maps to more than one Coordinated Universal Time (UTC). It occurs when the clock time is adjusted back in time, such as during the transition from a time zone’s daylight saving time to its standard time. When handling an ambiguous time, you can do one of the following:
- Make an assumption about how the time maps to UTC. For example, you can assume that an ambiguous time is always expressed in the time zone’s standard time.
- If the ambiguous time is an item of data entered by the user, you can leave it to the user to resolve the ambiguity.