Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition)

Author: Krzysztof Cwalina, Brad Abrams
4.8
All Stack Overflow 104
This Year Stack Overflow 4
This Month Stack Overflow 1

Comments

by anonymous   2017-11-20
@Amy just to be sure, that one https://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613
by anonymous   2017-10-01
There are many classes in the BCL that either violated both LSP and ISP. This is the hard part of designing a reusable class library that has to be used for so many different use cases, while trying to keep the API intuitive to work with. There are more factors at play here than simply the SOLID principles. The [Framework Design Guidelines](https://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613) give some great clues about this.
by anonymous   2017-08-20

After spending 10 minutes editing the question for formatting, I'm still going to downvote it. There's no way I'm going to read all that.

Go pick up a copy of


Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition)

by anonymous   2017-08-20

You don't need a list of 'bad' exceptions, you should treat everything as bad by default. Only catch what you can handle and recover from. CLR can notify you of unhandled exceptions so that you can log them appropriately. Swallowing everything but a black listed exceptions is not a proper way to fix your bugs. That would just mask them. Read this and this.

Do not exclude any special exceptions when catching for the purpose of transferring exceptions.

Instead of creating lists of special exceptions in your catch clauses, you should catch only those exceptions that you can legitimately handle. Exceptions that you cannot handle should not be treated as special cases in non-specific exception handlers. The following code example demonstrates incorrectly testing for special exceptions for the purposes of re-throwing them.

public class BadExceptionHandlingExample2 {
    public void DoWork() {
        // Do some work that might throw exceptions.
    }
    public void MethodWithBadHandler() {
        try {
            DoWork();
        } catch (Exception e) {
            if (e is StackOverflowException ||
                e is OutOfMemoryException)
                throw;
            // Handle the exception and
            // continue executing.
        }
    }
}

Few other rules:

Avoid handling errors by catching non-specific exceptions, such as System.Exception, System.SystemException, and so on, in application code. There are cases when handling errors in applications is acceptable, but such cases are rare.

An application should not handle exceptions that can result in an unexpected or exploitable state. If you cannot predict all possible causes of an exception and ensure that malicious code cannot exploit the resulting application state, you should allow the application to terminate instead of handling the exception.

Consider catching specific exceptions when you understand why it will be thrown in a given context.

You should catch only those exceptions that you can recover from. For example, a FileNotFoundException that results from an attempt to open a non-existent file can be handled by an application because it can communicate the problem to the user and allow the user to specify a different file name or create the file. A request to open a file that generates an ExecutionEngineException should not be handled because the underlying cause of the exception cannot be known with any degree of certainty, and the application cannot ensure that it is safe to continue executing.

Eric Lippert classifies all exceptions into 4 groups: Fatal, 'Boneheaded', Vexing, Exogenous. Following is my interpretation of Eric's advice:

  Exc. type | What to do                          | Example
------------|-------------------------------------|-------------------
Fatal       | nothing, let CLR handle it          | OutOfMemoryException
------------|-------------------------------------|-------------------
Boneheaded  | fix the bug that caused exception   | ArgumentNullException
------------|-------------------------------------|-------------------
Vexing      | fix the bug that caused exception   | FormatException from 
            | (by catching exception  because     | Guid constructor
            | the framework provides no other way | (fixed in .NET 4.0 
            | way of handling). Open MS Connect   | by Guid.TryParse)
            | issue.                              | 
------------|-------------------------------------|-------------------
Exogenous   | handle exception programmatically   | FileNotFoundException 

This is roughly equivalent to Microsoft's categorization: Usage, Program error and System failure. You can also use static analysis tools like FxCop to enforce some of these rules.

by anonymous   2017-08-20

Generally, the rule goes something like this:

  • Inheritance describes an is-a relationship.
  • Implementing an interface describes a can-do relationship.

To put this in somewhat more concrete terms, let's look at an example. The System.Drawing.Bitmap class is-an image (and as such, it inherits from the Image class), but it also can-do disposing, so it implements the IDisposable interface. It also can-do serialization, so it implements from the ISerializable interface.

But more practically, interfaces are often used to simulate multiple inheritance in C#. If your Processor class needs to inherit from something like System.ComponentModel.Component, then you have little choice but to implement an IProcessor interface.

The fact is that both interfaces and abstract base class provide a contract specifying what a particular class can do. It's a common myth that interfaces are necessary to declare this contract, but that's not correct. The biggest advantage to my mind is that abstract base classes allow you provide default functionality for the subclasses. But if there is no default functionality that makes sense, there's nothing keeping you from marking the method itself as abstract, requiring that derived classes implement it themselves, just like if they were to implement an interface.

For answers to questions like this, I often turn to the .NET Framework Design Guidelines, which have this to say about choosing between classes and interfaces:

In general, classes are the preferred construct for exposing abstractions.

The main drawback of interfaces is that they are much less flexible than classes when it comes to allowing for the evolution of APIs. Once you ship an interface, the set of its members is fixed forever. Any additions to the interface would break existing types implementing the interface.

A class offers much more flexibility. You can add members to classes that you have already shipped. As long as the method is not abstract (i.e., as long as you provide a default implementation of the method), any existing derived classes continue to function unchanged.

[ . . . ]

One of the most common arguments in favor of interfaces is that they allow separating contract from the implementation. However, the argument incorrectly assumes that you cannot separate contracts from implementation using classes. Abstract classes residing in a separate assembly from their concrete implementations are a great way to achieve such separation.

Their general recommendations are as follows:

  • Do favor defining classes over interfaces.
  • Do use abstract classes instead of interfaces to decouple the contract from implementations. Abstract classes, if defined correctly, allow for the same degree of decoupling between contract and implementation.
  • Do define an interface if you need to provide a polymorphic hierarchy of value types.
  • Consider defining interfaces to achieve a similar effect to that of multiple inheritance.

Chris Anderson expresses particular agreement with this last tenet, arguing that:

Abstract types do version much better, and allow for future extensibility, but they also burn your one and only base type. Interfaces are appropriate when you are really defining a contract between two objects that is invariant over time. Abstract base types are better for defining a common base for a family of types.

by anonymous   2017-08-20

The difference between VBA and VB.NET is just because VB.NET compiles continuously in the background. You'll get an error when you compile the VBA.

Like Jonathan says, when programming you can think of VB.NET as case-insensitive apart from string-comparisons, XML, and a few other situations...

I think you're interested in what's under the hood. Well, the .NET Common Language Runtime is case-sensitive, and VB.NET code relies on the runtime, so you can see it must be case-sensitive at runtime, e.g. when it's looking up variables and methods.

The VB.NET compiler and editor let you ignore that - because they correct the case in your code.

If you play around with dynamic features or late-binding (Option Strict Off) you can prove that the underlying run-time is case-sensitive. Another way to see that is to realise that case-sensitive languages like C# use the same runtime, so the runtime obviously supports case-sensitivity.

EDIT If you want to take the IDE out of the equation, you can always compile from the command-line. Edit your code in Notepad so it has ss and SS and see what the compiler does.

EDIT Quote from Jeffrey Richter in the .NET Framework Design Guidelines page 45.

To be clear, the CLR is actually case-sensitive. Some programming languages, like Visual Basic, are case insensitive. When the Visual Basic compiler is trying to resolve a method call to a type defined in a case-sensitive language like C#, the compiler (not the CLR) figures out the actual case of the method's name and embeds it in metadata. The CLR knows nothing about this. Now if you are using reflection to bind to a method, the reflection APIs do offer the ability to do case-insensitive lookups. This is the extent to which the CLR offers case-insensitivity.

by anonymous   2017-08-20

I prefer to name my virtual or abstract methods with the suffix Core, to indicate, that the method should contain the core logic to do something.

All argument checks and raising possible events I do in the method, that calls the Core-Methods.

  abstract class Animal {
    public void Walk() {
      // TODO: do something before walking 
      // possible Argument checks and event raising

      // custom logic implemented by each subclass
      WalkCore();

      // TODO: do something after walking
    }

    protected abstract void WalkCore();
  }

  class Dog : Animal {
    protected override void WalkCore() {
      // TODO: walk with 4 legs
    }
  }

  class Bird : Animal {
    protected override void WalkCore() {
      // TODO: walk with 2 legs
    }
  }

I think there is no offical naming guideline for this, and it´s up to you. But it should be consistent for all classes and virtual/abstract methods you define.

The "Framework Design Guidelines" suggest to use the Core suffix if you follow the Template Method and want to provide extensibility points.

by anonymous   2017-08-20

As you figured out yourself, the article you were originally looking for is called Design Guidelines for Developing Class Libraries on the MSDN.

Note that it also exists a great complete book on the very same topic, named Framework Design Guidelines. Actually, the MSDN page encourages you to have a look at this book if you want to go deeper:

For more information on design guidelines, see the "Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" book by Krzysztof Cwalina and Brad Abrams, published by Addison-Wesley, 2005.

alt text http://davesbox.com/images/books/FrameworkDesignGuidelines2ndEditionLarge.jpg

by anonymous   2017-08-20

The recommendation comes from the Framework Design Guidelines. This recommendation of Microsoft exists specially for reusable class libraries. In other words: frameworks like the .NET framework itself. If you are creating a line of business application, this guideline does not apply. It does not apply because it is unlikely that you will have versioning problems in a line of business application, because you have control over all the client code that talks to your classes / interfaces. This is of course not the case for reusable libraries.

Even the article of Phil Haack, where Will references to describes this:

Again, these guidelines are specific to Framework development (for statically typed languages), and not to other types of software development.

Removing versioning problems is therefore probably not a good argument for using base classes. Keeping code DRY however could be. However, using base classes and interfaces aren't mutually exclusive; you can let your abstract class ProductRepository implement IProductRepository. If you do this however, exposing anything else than IProductRepository would be a bad idea.