BRO1306: Constant, static readonly and non-private field names should be PascalCase¶
Constants, static readonly fields and public or internal fields are named MaxCount, not maxCount or
_maxCount. Replaces StyleCop SA1303, SA1311, SA1307 and SA1304.
Example¶
Before¶
internal class Retry
{
public const int maxAttempts = 3;
private static readonly int[] delays = { 100, 500, 2000 };
public int attempts;
public int NextDelay() => attempts < maxAttempts ? delays[attempts++] : -1;
}
After¶
internal class Retry
{
public const int MaxAttempts = 3;
private static readonly int[] Delays = { 100, 500, 2000 };
public int Attempts;
public int NextDelay() => Attempts < MaxAttempts ? Delays[Attempts++] : -1;
}
Why¶
Constants and static readonly fields act like fixed values of the type, and public fields are part of its surface
like properties, so they are named like properties. Private instance fields are camelCase instead
(BRO1303).
Configuration¶
Private constants and private static readonly fields can be camelCase instead (StyleCop issues
#2641 and
#3793), when the SDK's naming rules ask for it, like
BRO1303 follows them for private fields. The rule that counts is a dotnet_naming_rule whose symbols are
fields (kind field or *), private (accessibility private or *) and singled out by their modifiers: const (for
constants; static may be listed too), or static and/or readonly (for static readonly fields). Among those, the
lowest priority wins, then the rule with more required modifiers, then the name. If that rule's style is camel case
without a prefix, the fields are renamed to maxAttempts; with required_prefix = _, to _maxAttempts. A PascalCase
style and no such rule leave the default, PascalCase. Any other style (an s_ prefix, all upper case) leaves the names
as they are: StyleBro doesn't write it and never renames against it. A naming rule without required modifiers (all
private fields) doesn't count: StyleCop and most teams name constants differently from fields.
[*.cs]
dotnet_naming_rule.private_constants.symbols = private_constants
dotnet_naming_rule.private_constants.style = camel
dotnet_naming_rule.private_constants.severity = warning
dotnet_naming_symbols.private_constants.applicable_kinds = field
dotnet_naming_symbols.private_constants.applicable_accessibilities = private
dotnet_naming_symbols.private_constants.required_modifiers = const
dotnet_naming_style.camel.capitalization = camel_case
Public, internal and protected fields follow the SDK's naming rules too: the first dotnet_naming_rule that covers the
field (by its accessibility and its const/static/readonly modifiers, in Roslyn's order: priority, then the more
specific modifiers, accessibilities and symbol kinds, then the name; a rule without a severity doesn't count, a rule
turned off leaves StyleCop's default) decides. A camel-case style renames them to camelCase (BRO1303), a
PascalCase style to PascalCase (BRO1306), any other style leaves them as they are. YamlDotNet's
instance_fields_should_be_camel_case (protected, internal, ... instance fields) keeps its internal readonly
YamlAttributeOverrides overrides. Without a rule, StyleCop's defaults apply: PascalCase for public and internal
fields, camelCase for protected ones. The same defaults apply when nothing enforces the naming rules: a rule counts
for these fields only when IDE1006's severity, or without one the rule's own, is suggestion, warning or error
(dotnet_diagnostic.IDE1006.severity = none turns them all off; a silent rule needs IDE1006 at suggestion or
higher). To have an unenforced rule followed, raise its severity to suggestion.
stylebro_private_static_field_naming sets the style of both directly and wins over the naming rules: PascalCase
(StyleCop's), camelCase or _camelCase. stylebro-migrate --write sets it to PascalCase, because StyleCop
enforced that whatever the naming rules said: a repository with a camel-case naming rule for static fields and IDE1006
turned off ("StyleCop handles these") keeps its ProtocolVersion fields.
[*.cs]
stylebro_private_static_field_naming = PascalCase
stylebro_allow_leading_underscore_in_pascal_fields = true leaves every field whose name starts with _ alone
(_minCount stays): StyleCop's SA1303, SA1304, SA1307 and SA1311 only check that the first character isn't
lower-case, and the underscore is SA1309's. stylebro-migrate --write sets it to true when SA1309 is off, false
otherwise. Default false.
stylebro_rename_public_api = true also renames names other assemblies see (default false, see Not reported; the owner's decision in decisions.md).
How the fix works¶
The field is renamed everywhere in the solution. The new name drops leading underscores and capitalizes the first
letter: maxAttempts becomes MaxAttempts, _minCount becomes MinCount. Where a local or parameter with the new
name would hide the field, the reference is qualified (Retry.MaxAttempts, this.Attempts). A field may get the
name of its own type (static readonly SimulatorEvent _simulatorEvent becomes SimulatorEvent SimulatorEvent): C#
allows it, and SimulatorEvent.Create() still means the type. A member of an enclosing type with the new name doesn't
stop a nested type's field either, unless the nested type uses that member by name.
Checked: constants and static readonly fields of any accessibility, and public, internal and protected internal
fields. Protected fields are BRO1303's, and enum members are left out (StyleCop's SA1300)
and one-letter prefixes like s_count (SA1308).
Renaming a public field is a breaking change for code outside the solution, and a serializer may write a public field under its name (see below).
Not reported¶
- Where IDE1006 is suppressed: after
#pragma warning disable IDE1006(up to itsrestore), or with[SuppressMessage("Style", "IDE1006")]on the declaration, its member or type, or a global one targeting them. Code that opts out of the SDK's naming rules (a file copied from another project) keeps its names. - Fields in a class whose name ends in
NativeMethods(or a type nested in one), like StyleCop's SA1303, SA1304 and SA1307: they hold Win32 and COM names. Astatic readonlyfield starting lower-case is still reported, like SA1311, which doesn't skip them. - Interop names: SCREAMING_CASE constants and
static readonlyfields (S_OK), COM GUID fields (IID_...), and the fields of interop-shaped types ([StructLayout],[ComImport],[DllImport]methods); see BRO1308. - A non-private field whose name appears in code excluded by
#ifanywhere in the project (private fields: in their type, see below): disabled code isn't compiled, so the rename can't follow it. The same unsafe cases as BRO1303: a member with the new name in the type or a base type, two fields that would get the same name, attributes or a[Serializable]type, the old name in a string in the type (for public instance fields, anywhere inside a string, like a JSON example), in#if-excluded code, or as an inferred anonymous type member or tuple element name, and fields of a type with a generated part (a Razor component's code-behind class).
Reported but not fixed¶
If dotnet format leaves a finding, StyleBro kept it on purpose. The fix looks at the whole solution, other
projects included, which the analyzer can't see, and leaves a change that would break something there: the name in a
string or a nameof (reflection, serialization), a member that would hide it, a use in generated code. It offers no
code action for such a finding, and stylebro-migrate format lists each one with its reason
(Findings kept on purpose); its --verify-no-changes doesn't fail on
them. Change it by hand only after checking those uses, or suppress it or record it in the baseline.
- Public API (unless
stylebro_rename_public_api = true): Fields that other assemblies see, that is public, protected or protected internal in types that are visible too. Renaming them breaks code outside the solution. Internal code is renamed, also withInternalsVisibleTo. - A name that
nameof(...)produces: the rename would change the string (Ocelot built an HTTP header name withnameof(X_RateLimit_Limit), and a test read a private field withGetField(nameof(_components))). Looked for in the member for variables and parameters, in the type for fields; the fix also looks in the rest of the solution and leaves the warning when it finds one. - The old name appears in a string elsewhere in the solution. For a public or internal instance field any string,
also inside a longer one, like test JSON (
"{\"attempts\": 1}"): serializers such as Json.NET write public fields under their name, so the rename would change the output. For other fields only a string where code reaches a member by name (GetField("MaxAttempts"), see BRO1303). - Some reference would see another member with the new name, for example a property
MaxAttemptsin a derived class, which would hide the renamed field there. - Generated code uses the field (a source generator's output, a
.g.csfile), or its name is a string there.
Rename these by hand, or suppress the warning.
Compared with StyleCop¶
Same fields as StyleCop, with one diagnostic per field where StyleCop can report two (SA1307 with SA1311 or SA1304).
A constant or static readonly field that starts with _ is renamed too (_minCount becomes MinCount); StyleCop
leaves that one to SA1309. With stylebro_allow_leading_underscore_in_pascal_fields = true (what migrating sets when
SA1309 is off) it isn't, like StyleCop.