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

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

Comments

by idan   2020-09-09
Ignore the fact that this is a book about .NET, it's an excellent source of wisdom about the design of APIs:

https://www.amazon.com/dp/0321545613/ref=cm_sw_r_cp_apa_i_wn...

by anonymous   2019-07-21

Yes because you're potentially introducing unexpected behaviors. Objects should be constructed and free from exceptions and unexpected behavior. If the async actions are still happening after the object is constructed and the user is trying to use it...

Also, what if the async method is still running and the user decides to destroy the object?

A good read: http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613/ref=sr_1_1?s=books&ie=UTF8&qid=1314813265&sr=1-1

If your async method has to be async because it's a long running process then it needs to be moved to it's own method that gets called by the user, not when the object is instatiated.

You also have the potential issue of keeping a reference to the object if the user decides to destroy it before async method is done which means you could have a memory leak.

If this HAS to be done to initialize the object should be created using a factory or a builder.

by anonymous   2019-07-21

First, if you're developing an API for use by others, I'd recommend reading a book like: http://www.amazon.com/Framework-Design-Guidelines-Conventions-Development/dp/0321545613

Following those rules will avoid a large number of usability issues with your interface before any review is necessary.

Second, run a usability study with a few target developers, those who're likely to make use of this API, but haven't ever seen it before. Put them in front of the system and give them a few tasks, then watch how they go about figuring out how to do it. Their pain points will tell you where you need to make improvements.

by anonymous   2019-07-21

Organize your project cleanly into namespaces. Namespaces should not be too big, not too small. Make each namespace have a public "interface" (i.e. a set of public classes) and don't access internal implementation details of the namespace from other namespaces. Different namespaces usually address different parts of an application, e.g. you'll have namespaces related to UI, business logic, helper functionality, etc. The Framework Design Guidelines have some good suggestions how to design namespaces.

When you feel that your project grows too large, simply identify sets of namespaces that are clearly related to each other and move them to separate projects. Since other namespaces already just use the public interface of the moved namespaces, refactoring the namespaces into new projects is merely a file-move-operation.

by anonymous   2019-07-21

There are two common conventions.

the first is "User underscore as field marker" the second is "Use s_ for static fields and m_ for intance fields"

imo this is a religious question and onnly important thing is to not mix up both styles.

This book contains many good ideas about convention and design guidelines

http://www.amazon.de/Framework-Design-Guidelines-Conventions-Development/dp/0321545613/ref=sr_1_1?ie=UTF8&qid=1320395003&sr=8-1

by anonymous   2019-07-21

According to this book, which explains the problems they had when writting the first versions of the framework; at the beggining of the .Net framework lifecycle there were lots of discussions about guidelines (naming conventions, interfaces vs abstract classes, etc).

That is why "old namespaces" such as the Interop services contain implementations that are against the rules that you can find today, basically those rules where learned and implemented while implementing the code itself!

The .Net framework still has old legacy code that cannot be changed because of the implications it would have in old applications.

I believe there was some more info in this book as well, but not too sure now.

by anonymous   2019-07-21

The almost-canonical one is "Design Guidelines for Developing Class Libraries", which is pretty much the online version of this book. There's a lot of good information in there.

by anonymous   2019-01-13

It is considered a best practice to NEVER return null when returning a collection or enumerable. "ALWAYS" return an empty enumerable/collection. It prevents the aforementioned nonsense, and prevents your car getting egged by co-workers and users of your classes.

In general that's correct, but there are cases when returning null isn't wrong. so the "ALWAYS" has to be omitted.

Also, a great source is the Framework Design Guidelines 2nd Edition (pg. 256):

DO NOT return null values from collection properties or from methods returning collections. Return an empty collection or an empty array instead.

by anonymous   2018-03-19

Simple Injector does not contain an IContainer abstraction, because that would be useless:

  • It would be useless for Simple Injector to define it, because your code would in that case still depend on the library (since Simple Injector defines that abstraction), and this causes a vendor lock-in, which Simple Injector tries to prevent.

  • Any code you write, apart from the application's Composition Root, should not depend on the container, nor on an abstraction over the container. Both are implementations of the Service Locator anti-pattern.

  • You should NOT use a DI library when unit testing. When unit testing, you should manually inject all fake or mock objects in the class under test. Using a container only complicates things. Perhaps you are using a container, because manually creating those classes is too cumbersome for you. This might indicate problems with your code (you might be violating the Single Responsibility Principle) or your tests (you might be missing a factory method to create the class under test).

  • You might use the container for your integration tests, but you shouldn't have that many integration tests in the first place. The focus should be on unit tests and this should be easy when applying the dependency injection pattern. On top of that, there are better ways of hiding the container from your integration tests, compared to depending on a very wide library-defined interface.

  • It is trivial to define such interface (plus an adapter) yourself, which justifies not having it in the library. It is your job as application developer to define the right abstractions for your application as stated by the Dependency Inversion Principle. Libraries and frameworks that tend to do this will fail most of the time in providing an abstraction that works for everyone.

  • The library itself does not use that abstraction and a library should, according to the Framework Design Guidelines, in that case not define such abstraction for you. As stated in the previous point, Simple Injector would get the abstraction wrong anyway.

  • Last but not least, the Simple Injector container does actually implement System.IServiceProvider which is defined in mscorlib.dll and can be used for retrieving service objects.

by anonymous   2018-03-19

From the Framework Design Guidelines 2nd Edition (pg. 256):

DO NOT return null values from collection properties or from methods returning collections. Return an empty collection or an empty array instead.

Here's another interesting article on the benefits of not returning nulls (I was trying to find something on Brad Abram's blog, and he linked to the article).

Edit- as Eric Lippert has now commented to the original question, I'd also like to link to his excellent article.

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.