📜 ⬆️ ⬇️

C #. Sort Type Members with ReSharper


There are some agreements concerning the structure of a class, and the order in which its members should be located.
Here, for example, the rules that StyleCop uses, perhaps your company has its own.
Maintaining the structure manually is quite difficult, boring and time consuming, especially when there are quite a large number of properties, fields, methods, etc. in the class.
This post will discuss how to automate this process using ReSharper.

Problem


Sometimes with fast coding, we forget about sorting members and it turns out something like this.
Porridge
public class DemoClass : IEquatable<DemoClass> { public string SecondProperty { get; set; } private bool fieldBool; public override int GetHashCode() { return base.GetHashCode(); } public string SomeProperty { get; set; } public void DoSomething() { } private string fieldString; public DemoClass(bool fieldBool, string fieldString) { this.fieldBool = fieldBool; this.fieldString = fieldString; } public static void DoSomethingStatic() { } public bool Equals(DemoClass other) { throw new NotImplementedException(); } } 


Sort by default


But, fortunately, in ReSharper there is a tool Code Cleanup . And if you run it with the Reorder Type Members option enabled , we get a class with ordered members.
After
  public class DemoClass : IEquatable<DemoClass> { private bool fieldBool; private string fieldString; public DemoClass(bool fieldBool, string fieldString) { this.fieldBool = fieldBool; this.fieldString = fieldString; } public string SomeProperty { get; set; } public string SecondProperty { get; set; } public bool Equals(DemoClass other) { throw new NotImplementedException(); } public override int GetHashCode() { return base.GetHashCode(); } public void DoSomething() { } public static void DoSomethingStatic() { } } 


Customizable rules


But what if the default sorting does not suit us, and is different from the one that you have adopted as a standard?
In this case, ReSharper allows you to override the rules used by default.
Go to ReSharper -> Options -> Code Editing -> C # -> Type Member Layout and enable Custom Layout.
Before us will open XML which describes the sorting order.
Example
 <Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns"> <Pattern> <!--static fields and constants--> <Entry> <Match> <Or Weight="100"> <Kind Is="constant"/> <And> <Kind Is="field"/> <Static/> </And> </Or> </Match> <Sort> <Kind Order="constant field"/> </Sort> </Entry> <Entry> ... </Entry> </Pattern> <Pattern> ... <Pattern> </Patterns> 


The Patterns element, the parent element, can contain within itself a set of elements of the Pattern, which, in turn, can contain many Entry elements.
The Entry element is a record with which we will perform some actions. The order of the Entry in the Pattern element affects the order of the elements.
Suppose we need the following order: constructors, methods, other members.
For this we can write the following rule:
Example
 <?xml version="1.0" encoding="utf-8"?> <Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns"> <Pattern> <Entry> <Match> <Kind Is="constructor" /> </Match> </Entry> <Entry> <Match> <Kind Is="method" /> </Match> </Entry> <Entry /> </Pattern> </Patterns> 


Each Entry element contains a nested Match element in which we describe what we are looking for. We can specify more detailed rules for the search and use the logical operators Or, And, Not and operands:
OperandMeanings
<Kind Is = "..." />The Is attribute can be a class, struct, interface, enum, delegate, type, constructor, destructor, property, indexer, method, operator, field, constant, event, member
<Name Is = "..." [IgnoreCase = "true / false"] />The Is attribute may contain a regular expression
<HasAttribute CLRName = "..." [Inherit = "true / false"] />The CLRName attribute may contain a regular expression.
<Access Is = "..." />The attribute Is can be public, protected, internal, protected-internal, private
<Static />
<Abstract />
<Virtual />
<Override />
<Sealed />
<Readonly />
<ImplementsInterface CLRName = "..." />The CLRName attribute may contain a regular expression.
<HandlesEvent />

Also, each Entry element can contain a Sort, which affects the sorting of the found members within one Entry.
For example, we need to find static fields and constants and arrange them at the very beginning of the class. And also sort them by name. To do this, we need to add the following entry to the beginning of the pattern.
Example
  <Entry> <Match> <Or> <Kind Is="constant" /> <And> <Kind Is="field" /> <Static /> </And> </Or> </Match> <Sort> <Name /> </Sort> </Entry> 


Sorting can be by several values; the following example sorts all methods by access level and name.
Example
  <Entry> <Match> <Kind Is="method"/> </Match> <Sort> <Access Order="public internal protected-internal protected private" /> <Name/> </Sort> </Entry> 


Grouping


Grouping is also supported.
Assume that the methods must be divided into static and instance, sorted by name and wrapped in the appropriate regions. To do this, we need to create two different Entry elements with a Group child element:
Example
  <Entry> <Match> <And> <Kind Is="method" /> <Static/> </And> </Match> <Group Region="Static methods" /> </Entry> <Entry> <Match> <Kind Is="method" /> </Match> <Group Region="Instance methods" /> </Entry> 


What if all the members of the interfaces that the class implemented are sorted by interface name and wrapped in a region with the name of this interface? For this we could do this:
Example
  <Entry> <Match> <Kind Is="member" /> <ImplementsInterface/> </Match> <Sort> <ImplementsInterface/> </Sort> <Group> <ImplementsInterface Region="${ImplementsInterface} Members" /> </Group> </Entry> 


In this example, the variable $ {ImplementsInterface} will be equal to the name of the interface.
Also, the Pattern element supports the RemoveAllRegions attribute, which can be set to true or false (false by default). If it is set to true, then when grouping, all regions that were before it will be deleted.

Weight


What if the condition (Match) meet several types at once?
Suppose we want to group all private members into one region, except for the methods. We could write the following rule:
Example
  <Entry> <Match> <Access Is="private" /> </Match> <Group Region="Private members" /> </Entry> <Entry> <Match> <Kind Is="method" /> </Match> <Group Region="All methods" /> </Entry> 


But, if we have a private method, then it falls under both rules. The first rule will be applied earlier and under the second rule all methods except private will fall. As a result, the region that will contain private members will also contain private methods.
Each operand successfully completed gives 1 point of weight by default and these points are added together. In this case, in each of the elements there is one operand: <Access Is = "private" /> and <Kind Is = "method" />, respectively. If we want the second rule to execute, we can use the Weight attribute on the operand.
Example
  <Entry> <Match> <Access Is="private" /> </Match> <Group Region="Private members" /> </Entry> <Entry> <Match> <Kind Is="method" Weight="5"/> </Match> <Group Region="All methods" /> </Entry> 


Thus, the second event will have more weight, and everything will be grouped as intended.

Patterns


At the very beginning, I spoke about the Pattern element, it can also contain a Match element and this element determines where the rules that the pattern contains will act.
And now, imagine the situation, when in all interfaces, we need another sorting. In such a situation, we could create a separate Pattern element.
Example
  <Pattern> <Match> <Kind Is="interface" /> </Match> ... </Pattern> 


All the rules that will be described in the above pattern will affect only the interfaces.
')

Conclusion


In this post, I wanted to show you some of the features of ReSharper that may make your life easier.
This post does not pretend to have comprehensive documentation on this issue.
Sources:

Source: https://habr.com/ru/post/208464/


All Articles