We use the same coding style conventions as outlined in .NET Framework Coding Styles, with the following additions:
- DO put one type per file, including nested types. Files containing a nested type, should follow the
Parent.NestedType.csconvention. Generic types should follow theGenericWithOneTypeParameter`1.cs,GenericWithTwoTypeParameters`2.csconvention. If you have a single generic type,GeneraticWithOneTypeParameter.csis acceptable. - DO NOT use regions.
- DO sort members in classes in the following order; fields, constructors, events, properties and then methods.
- DO flavor private fields over private properties.
- DO case internal fields as
PascalCasednot_camelCased.
The majority of the guidelines, where possible, are enforced via the .editorconfig in the root the repository.
-
DO use constructor injection over property/field injection.
-
DO use MEF imports over direct usage of
IComponentModel.
- DO flavor
IVsUIService<T>andIVsUIService<TService, TInterface>over usage ofIServiceProvider.
IVsUIService enforces UI thread access which prevents accidental RPC calls from a background thread.
- DO flavor
IVsService<T>andIVsService<TService, TInterface>over usage ofIAsyncServiceProvider.
IVsService ensures casts are performed on the UI thread which prevents accidental RPC calls from a background thread.
-
DO flavor
HResultoverVSConstantsand raw constants. -
DO flavor
HierarchyIdoverVSConstants.VSITEMIDand raw constants.
-
DO flavor a single Assert per unit test.
-
DO use the
Method_Setup_Behaviornaming style for unit tests, for example,GetProperty_NullAsName_ThrowsArgumentorCalculateValues_WhenDisposed_ReturnsNull. -
DO flavor static
CreateInstancefor creating the object under test versus directly calling the constructor
This reduces the amount of refactoring/fixup needed when adding a new import to a service.
- DO NOT mix snapshot and "live" project data in the same component.
For example, listening to data flow blocks from IProjectSubscriptionService and then reading properties from ProjectProperties within the callback will lead to inconsistent results. The dataflow represents a "snapshot" of the project from changes in the past, whereas ProjectProperties represents the actual live project. These will not always agree. The same applies to consuming other CPS APIs from within a dataflow block, the majority of them use live data to provide results and hence will return results inconsistent with the snapshot that you are reading in the dataflow.
- DO NOT parse or attempt to reason about the values of properties that make up the dimensions for a project configuration;
$(Configuration),$(Platform)and$(TargetFramework), and their plural counterparts;$(Configurations),$(Platforms)and$(TargetFrameworks).
These properties are user "aliases" and should only be used for conditions, display and grouping purposes. Instead, the project system should be using their canonical equivalents; $(PlatformTarget) instead of $(Platform), and $(TargetFrameworkMoniker) instead of $(TargetFramework).
-
DO follow the 3 threading rules inside Visual Studio.
-
DO NOT call
IProjectThreadingService.ExecuteSynchronouslyorJoinableTaskFactory.Runfrom a ThreadPool thread that marshals to another thread (such as viaJoinableTaskFactory.SwitchToMainThreadAsyncor calling an STA-basedIVsXXXobject). If you synchronously block on other async code, often that code needs to run or finish on a ThreadPool thread. When the number of threads in the ThreadPool reaches a certain threshold, the ThreadPool manager slows down thread creation and only adds a new thread to the pool every 250 - 500ms. This can result in random UI deadlocks for short periods of time while the code on the UI thread waits for a new thread to be spun up. See ThreadPool Starvation for more information. -
AVOID marking
awaitcalls withConfigureAwait(true)orConfigureAwait(false).
We follow the Visual Studio guidelines around ConfigureAwait usage.