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.cs
convention. Generic types should follow theGenericWithOneTypeParameter`1.cs
,GenericWithTwoTypeParameters`2.cs
convention. If you have a single generic type,GeneraticWithOneTypeParameter.cs
is acceptable. - DO NOT use regions.
- DO sort members in classes in the following order; fields, constructors, events, properties and then methods.
- DO favor private fields over private properties.
- DO case internal fields as
PascalCased
not_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 favor
IVsUIService<T>
andIVsUIService<TService, TInterface>
over usage ofIServiceProvider
.IVsUIService
enforces UI thread access which prevents accidental RPC calls from a background thread. -
DO favor
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 favor
HResult
overVSConstants
and raw constants. -
DO favor
HierarchyId
overVSConstants.VSITEMID
and raw constants.
-
DO favor a single Assert per unit test.
-
DO use the
Method_Setup_Behavior
naming style for unit tests, for example,GetProperty_NullAsName_ThrowsArgument
orCalculateValues_WhenDisposed_ReturnsNull
. -
DO favor static
CreateInstance
for creating the object under test versus directly calling the constructorThis 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 fromProjectProperties
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)
and$(TargetPlatformMoniker)
instead of$(TargetFramework)
-
DO follow the 3 threading rules inside Visual Studio.
-
DO NOT call
IProjectThreadingService.ExecuteSynchronously
orJoinableTaskFactory.Run
from a ThreadPool thread that marshals to another thread (such as viaJoinableTaskFactory.SwitchToMainThreadAsync
or calling an STA-basedIVsXXX
object).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
await
calls withConfigureAwait(true)
orConfigureAwait(false)
.We follow the Visual Studio guidelines around
ConfigureAwait
usage.