📜 ⬆️ ⬇️

Recommendations for writing C # code from Aviva Solutions

I present to your attention the translation of the document " Coding Guidelines for C # 3.0, 4.0 and 5.0 ".

The purpose of creating this list of rules is to attempt to set standards for writing C # code that would be convenient and practical at the same time. Of course, we practice what we have created. These rules are one of those standards that underlie our daily work in AvivaSolutions. Not all of these rules have a clear rationale. Some of them are simply accepted as standards.

Static code analyzer VisualStudio (which is also known as FxComp) and StyleCop can automatically apply many of the encoding and formatting rules by analyzing compiled assemblies. You can configure them so that the analysis is done at compile time or an integral part of continuous or daily assembly. This document simply adds additional rules and guidelines, but its supporting site, www.csharpcodingguidelines.com, provides a list of code analysis rules, depending on which code base you are working with.

Why should I use it?


Some people perceive the standards of writing code as certain restrictions that limit the freedom of creativity. But nevertheless, this approach has been justified and tested for many years. Why? Because not every developer knows that:
')

Basic principles


There are many unobvious things that I came across in my work as a consultant and that deserve mention in at least one recommendation for writing code. Unfortunately, the scope of this manual should be within reasonable limits. However, whatever some novice developers think, if I have not mentioned something in this document, this does not mean that it does not deserve attention.

With this in mind, you should understand that this document is not able to cover all situations that you may encounter. In any case of dispute, you should refer to the basic principles that apply in any situation, regardless of context. These include:


Despite the fact that the code may look very good, see if it is understandable to other developers, if it has the behavior that you should expect from it, whether it is trying to solve those problems that may arise in the future. If this is not the case, then it is worth thinking about refactoring.

How do i get started?



Why did you create it?


The idea came in 2002 when Vick Hartorg (Philips Medical System) and I was tasked with writing coding standards for C # 1.0. Since that time, I regularly added, deleted and changed rules based on experience, feedback from colleagues and new plug-ins presented in Visual Studio 2010. In addition, after reading Robert Martin ’s book “Clean Code: Creating, Analyzing and Refactoring”, I caught fire with his ideas and decided to include some of them as rules. I would like to note that this document is by no means a replacement for his book. I sincerely recommend reading his book in order to firmly grasp the principles underlying his recommendations. In addition, I decided to supplement the principles of writing code with some design principles. They are too important to ignore, and have a great influence on the attainment of high quality code.

Are these guidelines standards?


This document does not state that projects should strictly adhere to these recommendations. Nor does it state that these recommendations are more important than others. However, we recommend that you yourself decide which recommendations are important, which deviations from the standard can be allowed in the project, who will be a consultant in case of doubt, and what typesetting to use for the source code. Obviously, it is you who must make these decisions before embarking on real work.

To help you choose, I assigned the importance of each recommendation:

- The rule that you should never neglect and which applies to all situations.
- It is strongly recommended to comply with this rule.
- It is recommended to follow this rule, but it does not apply to all situations.

In conclusion, I want to say that there is no need for all the generated code to meet the standards. However, if it is possible to modify the templates used to generate the code, try to ensure that they create code that best complies with these guidelines.

Feedback and Disclaimer


This document was drafted largely due to the great contributions of community members, blog articles, online discussions and many years of development in C #. If you have questions, comments and suggestions, email me at dennis.doomen@avivasolutions.nl or on Twitter at http://twitter.com/ddoomen . I will try to regularly review and re-publish this document in accordance with new ideas, experiences and comments.

I want to note that these recommendations only reflect my vision of the correct C # code. Aviva Solutions is not responsible for any direct or indirect damages caused by the application of the standards described in this document.
Permission is granted to copy, adapt and distribute this document and brief references to this manual for non-commercial purposes and for internal use. But you cannot distribute this document, publish or distribute any adapted copies of this document for commercial purposes without prior written permission from Aviva Solutions.

Class design guidelines


AV1000 Class or interface must have a single purpose.

The class or interface must have a single purpose within the system in which it is used. As a rule, a class serves one of the purposes: either it describes a type, for example, email or ISBN (international standard book number), or it is an abstraction of some business logic, or it describes a data structure, or is responsible for interaction between other classes. He should never combine these tasks. This rule is known as the Principle of Unified Responsibility , one of the principles of SOLID.

Tip : A class with the word “And” in the title is a clear violation of this rule.
Tip : For interaction between classes, use design patterns . If you fail to apply any of the patterns to the class, perhaps it takes on too much responsibility.
Note : If you create a class that describes a type, you can greatly simplify its use if you make it immutable.

AV1001 Create new class instances using the constructor so that you end up with a completely ready-to-use object. .

The created object should not need to set additional properties before being used for whatever purposes it is planned to use. Moreover, if the constructor needs more than three parameters (which violates rule AV1561), it is possible that the class takes on too much responsibility (violation of rule AV1000).

AV1003 The interface should be small and focused on solving one problem.

The interface must have a name that clearly describes its purpose or the role it performs in the system. Do not combine loosely coupled elements into one interface just because they belong to the same class. Build interfaces based on the functionality for which the called methods are responsible or on the basis of the specific task that this interface performs. This rule is better known as Interface Segregation Principle .

AV1004 Use an interface, not a base class, to support multiple implementations.

If you want to set an extension point for your class, set it as an interface, not a base class. You do not want to force users of this extension point to make their own implementations based on a base class that may behave in an undesirable way. However, for their convenience, you can create a default implementation (abstract class) that can serve as a starting point.

AV1005 Use the interface to implement loose coupling between classes.

Interfaces are a great tool for implementing loose coupling between classes.


AV1008 Avoid static classes

With the exception of the static classes that are used to create extension methods, static classes very often result in bad code. In addition, they are very difficult, if not impossible, to test in isolation until you resort to some very sophisticated tools.

Note : If you really need a static class, mark it as static . In this case, the compiler will block the creation of instances of this class and initialize your class before first accessing it. This will save you from having to use a private constructor.

AV1010 Do not hide inherited items behind the new keyword

This not only contradicts polymorphism , one of the most important principles of object-oriented programming, but also makes child classes difficult to understand. Consider the following two classes:

 public class Book { public virtual void Print() { Console.WriteLine("Printing Book"); } } public class PocketBook : Book { public new void Print() { Console.WriteLine("Printing PocketBook"); } } 

In this case, the class will have the wrong behavior that you would expect from it when using it:

 PocketBook PocketBook = new PocketBook (); pocketBook.Print (); //  "Printing PocketBook" ((Book)pocketBook).Print(); //  "Printing Book" 

Here it is shown that the Print method has different behavior depending on whether it is called via a reference to the base class or was called as a method of the derived class.

AV1011 Functions that use the base type must be able to use the subtypes of the base type without knowing it.

In other words, the behavior of inherited classes should not contradict the behavior specified by the base class, that is, the behavior of the inherited classes should be expected for code that uses a variable of the base type. The most well-known example of a violation of this rule is the NotImplementedExeption exception, when it occurs when a base class method is overridden.

Note : This principle is also known as the Barbara Liskov substitution principle, one of the SOLID principles.

AV1013 Do not refer to derived classes from the base class

The presence of dependencies in the parent class on its child classes violates the principles of object-oriented programming and prevents other developers from inheriting from your base class.

AV1014 The object must have limited knowledge of other objects that are not directly related to this object.

If your code resembles the code below, then you are breaking the Law of Demeter .

 someObject.SomeProperty.GetChild().Foo(); 

An object should not open access to the classes on which it depends, because user objects can incorrectly use properties and methods to gain access to objects behind it. By doing so, you allow the code being called to integrate into one with the class you are using. Thus, you limit the possibility of replacing one implementation with another in the future.

Note : Using a class that implements a fluid interface (Fluent Interface) may seem like a violation of this rule. But the methods being called simply pass the call context to the next link. Thus, it does not cause controversy.
Exception : When using inversion controls and dependency injection frameworks, it is often required that the dependencies are exposed as public properties. As long as these properties are not used for anything other than the implementation of injection dependencies, I would not consider this as a violation of the rules.

AV1020 Avoid Bidirectional Dependencies

Bidirectional dependence means that the two classes are aware of each other’s public methods or depend on each other’s internal behavior. Refactoring or replacing one of these two classes requires changes in both classes and may entail a lot of unexpected work. The most obvious solution is to create an interface for one of these classes and use injection dependencies.

Exception : Domain models (Domain Model) used in domain-based design (Domain Driven Design) may use bidirectional dependencies that describe associations from the real world. In such cases, I try to make sure that they are really necessary, and as far as possible I try to avoid them.

AV1025 Classes Must Have Condition and Behavior

If there are many classes in your repository that serve only to describe the data, then most likely you have several (static) classes that contain a lot of processing logic for this data (see rule AV1008). Use the principles of object-oriented programming as recommended in this section, move your data processing logic to the data it uses.

Exception : The only exception to this rule is the classes used to transfer data between application subsystems, also called Data Transfer Objects , or classes that serve as a wrapper for method parameters.

Recommendations for designing class members


AV1100 Class properties must be able to be set in any order.

Properties should not depend on other properties. In other words, there should be no difference in what property we set in the first place. For example, first DataSouse , then DataMember or vice versa.

AV1105 Use method instead of property

Use methods instead of properties if:


Exception : Filling the internal cache or implementing lazy-loading are good exceptions to this rule.

AV1110 Do not use mutually exclusive properties.

If you have properties that cannot be used at the same time, it means that they are two mutually exclusive concepts. Even if these concepts may have some common logic and state, it is obvious that they have different rules that do not fit together.

, , . .

AV1115

, ( AV1000), .

AV1125 , ,

(stateful object) – , , . , - , . , – , .

HttpContext.Current ASP.NET. HttpContext . , – (Isolate the Ugly Stuff) — .

AV1130 IEnumerable ICollection -

, , , . IEnumerable , , ICollection .

: .Net 4.5, IReadOnlyCollection , IReadOnlyList IReadOnlyDictionary<TKey, TValue> .

AV1135 , , , null

null , . . , null , , string.IsNullOrEmpty() .

AV1137 ,

, . , , , IConfiguration . , , , . , .

AV1140 , ,

, , ISBN ( ), , , , , . , -. .


AV1200

, , if , . . , . , .

AV1202

, , , .

AV1205 ,

, null , ArgumentNullException ArgumentException — .

AV1210

, Exception , SystemException . .

AV1215

, async/await Task , :



AV1220 null

, , null . , , , , , , null . , , , .

 event EventHandler<NotifyEventArgs> Notify; void RaiseNotifyEvent(NotifyEventArgs args) { EventHandler<NotifyEventArgs> handlers = Notify; if (handlers != null) { handlers(this, args); } } 

: , . , :

 event EventHandler<NotifyEventArgs> Notify = delegate {}; 

AV1225

. , , On . , TimeChanged OnTimeChanged .

: , , . , .

AV1230

The property change notification event should have a name like PropertyChangedwhere the Propertyproperty name with which this event is associated should be changed.

Note : If your class has many properties that require corresponding events, try implementing an interface instead INotifyPropertyChanged. It is often used in the Presentation Model and Model-View-ViewModel patterns .

AV1235 Do not send nullas an argument when calling an event

Often an event handler is used to handle similar events from multiple senders. In this case, the argument passed is used to pass the context of the event call. Always send a reference to the context (usually this) when calling an event. In addition, do not send nullwhen calling an event if it does not have data. If the event has no data, send EventArgs.Emptyinstead null.

Exception : For static events, the argument passed must be null.

AV1240 Use generic constraints if possible.

Instead of casting and converting a type from specific to general and vice versa, use a keyword whereor operator asto cast an object to a specific type. For example:

 class SomeClass {} //  class MyClass<T> { void SomeMethod(T t) { object temp = t; SomeClass obj = (SomeClass) temp; } } //  class MyClass<T> where T : SomeClass { void SomeMethod(T t) { SomeClass obj = t; } } 

AV1250 LINQ- ,

:

 public IEnumerable<GoldMember> GetGoldMemberCustomers() { const decimal GoldMemberThresholdInEuro = 1000000; var q = from customer in db.Customers where customer.Balance > GoldMemberThresholdInEuro select new GoldMember(customer.Name, customer.Balance); return q; } 

LINQ- , q , , , . , , foreach - , , GoldMember . , == , GoldMember . LINQ-, ToList() , ToArray() .


AV1500 7

, 7 , . , , . , , . , , , , . , .

AV1501 private , internal

, , . , public.

AV1502

, , customer.HasNoOrder , . For example:

 bool hasOrders = !customer.HasNoOrders; 

, , .

AV1505

DLL Company.Component.dll , Company — , Component — , . For example:

 AvivaSolutions.Web.Controls.dll 

AvivaSolutions.Web.Binding , . AvivaSolutions.Web.Binding.dll .

: , Core . . : AvivaSolutions.Consulting.Core.dll .

AV1506 ,

.

AV1507

: , , .

AV1508 , ,

, . – . – , . For example:

 //   MyClass.cs public partial class MyClass {...} //   MyClass.Designer.cs public partial class MyClass {...} 

AV1510 using

. , :

 var list = new System.Collections.Generic.List<string>(); 

:

 using System.Collections.Generic; var list = new List<string>(); 

, using :

 using Label = System.Web.UI.WebControls.Label; 

AV1515 «»

, , . For example:

 public class Whatever { public static readonly Color PapayaWhip = new Color(0xFFEFD5); public cons tint MaxNumberOfWheels = 18; } 

, , . , . For example:

 mean = (a + b) / 2; //   WhaitMilliseconds(waitTimeInSeconds * 1000); //     

, .

 public class SomeSpecialContainer { public const int MaxItems = 32; public const int HighWaterMark = 3 * MaxItems / 4; // 75% } 

: .

AV1520 var ,

var , LINQ- var . , :

 var i = 3; //   ? int? uint? float? var myfoo = MyFatoryMethod.Create(“arg”); //  ,    //   .  , //   ,   // c  ,    //   

var :

 var q = from order in orders where order.Items > 10 and order.TotalValue > 1000; var repository = new RepositoryFactory.Get<IOrderRepository>(); var list = new ReadOnlyCollection<string>(); 

. var « » .

AV1521

C VisualBasic, . , .

AV1522

:

 var result = someField = GetSomeMethod(); 

AV1523

:

 var startInfo = new ProcessStartInfo(“myapp.exe”); startInfo.StandardOutput = Console.Output; startInfo.UseShellExecute = true; 

Use object initializer :

 var startInfo = new ProcessStartInfo(“myapp.exe”) { StandardOutput = Console.Output, UseShellExecute = true }; 

Instead of creating a collection in this way:

 var countries = new List<string>(); countries.Add(“Netherlands”); countries.Add(“United States”); 

Use the collection initializer :

 var countries = new List<string> { “Netherlands”, “United States” }; 

AV1525 Do not make an explicit comparison with trueor false

Comparing a Boolean value with trueor false- this is usually a bad programming style. As an example:

 while (condition == false) // ,   while (condition != true) //   while (((condition == true) == true) == true) //   ? while (condition) // OK 

AV1530 Do not change the loop variable foror foreachinside the loop body.

Updating the loop variable inside the loop body causes the code to become confusing. Especially if the variable changes in more than one place. This rule also applies to the loop foreach, although after the end of the iteration, the enumerator will detect the change of the collection and will throw an exception.

 for (int index = 0; index < 10; ++index) { if (some condition) { index = 11; // !    'break'  'continue'. } } 

AV1532

, , , , . , LINQ-, from .

AV1535 if , else , while , for , foreach case

, , :

 if (b1) if (b2) Foo(); else Bar(); //   'if'  'else'? 

:

 if (b1) { if (b2) { Foo(); } else { Bar(); } } 

AV1536 default switch/case

default , . , , InvalidOperationException , , case . , .

 void Foo(string answer) { switch (answer) { case "no": Console.WriteLine("You answered with No"); break; case "yes": Console.WriteLine("You answered with Yes"); break; default: // Not supposed to end up here. throw new InvalidOperationException("Unexpected answer " + answer); } } 

AV1537 if-else-if else

For example:

 void Foo(string answer) { if (answer == "no") { Console.WriteLine("  "); } else if (answer == "yes") { Console.WriteLine("  "); } else { //   ,    ?  ? //  ,    InvalidOperationException. } } 

AV1540 return

— , . . AV1500, return . , , return , .

AV1545 if-else ()

. , :

 bool pos; if (val > 0) { pos = true; } else { pos = false; } 

:

 bool pos = (val > 0); //  

Instead:

 string result; if (someString != null) { result = someString; } else { result = “Unavailable”; } 

Write:

 return someString ?? “Unavailable”; 

AV1547

Consider the following example:

 if (member.HidesBaseClassMember && (member.NodeType != NodeType.InstanceInitializer)) { // -  } 

, , . , , , .

 if (NonConstructorMemberUsesNewKeyword(member)) { // -  } private bool NonConstructorMemberUsesNewKeyword(Member member) { return (member.HidesBaseClassMember && (member.NodeType != NodeType.InstanceInitializer) } 

, , . , .

AV1551

, . :

 public class MyString { private string someText; public MyString(string text) { this.someText = text; } public int IndexOf(string phrase) { return IndexOf(phrase, 0, someText.Length); } public int IndexOf(string phrase, int startIndex) { return IndexOf(phrase, startIndex, someText.Length - startIndex ); } public virtual int IndexOf(string phrase, int startIndex, int count) { return someText.IndexOf(phrase, startIndex, count); } } 

MyString IndexOf , . , . , this() . , .

: , , protected virtual , .

AV1553 ,

C# 4.0 – AV1551 :

 public virtual int IndexOf(string phrase, int startIndex = 0, int count = 0) { return someText.IndexOf(phrase, startIndex, count); } 

, null . , , string , list collections null ( AV1135). .

: . , .
: , , . . Eric Lippert for more information.

AV1555 Avoid Using Named Arguments

Named C # 4.0 arguments have been created to make it easy to call COM components that are known for offering tons of optional parameters. If you need named arguments to improve the readability of a call for a method, this method most likely does too much and needs to be refactored.

The only exception where named arguments improve readability is when the object's constructor is invoked, as in the example below:

 Person person = new Person ( firstName: "John", lastName: "Smith", dateOfBirth: new DateTime(1970, 1, 1) ); 

AV1561 ,

, . , , . - .

AV1562 ref out

. .

AV1564 ,

:

 public Customer CreateCustomer(bool platinumLevel) {} 

, , :

 Customer customer = CreateCustomer(true); 

, , , . .

AV1568

. , , , , .

AV1570 , as

as , null. Failure to comply with this requirement may lead to an exception NullReferenceExceptionat a much later stage of program execution, if the object does not implement the required interface.

AV1575 Do not leave commented out code points.

Never send commented out code to the repository. Instead, use a task tracking system to keep track of what work needs to be done. No one later will guess what this or that block of commented code is for. Has it been temporarily commented out for testing? Was it copied as an example? Should I remove it?

Naming Guidelines


AV1701

, .


: , , , . Visual Studio , .

AV1702

Example
,AppDomain
InterfaceIBusinessService
()ErrorLevel
()FatalError
EventClick
listItem
MainPanel
MaximumItems
maximumItems
Read-onlyRedValue
listOfValues
MethodToString
System.Drawing
ParametertypeName
TView
PropertyBackColor

AV1704 ,

.

AV1705

, g_ s_ . , , . : _currentUser , mUserName , m_loginTime .

AV1706

, OnButtonClick OnBtnClick . , «i» «q». , «index» «query».

: . , UI UserInterface .

AV1707 , ,


AV1708 ,

: SearchExamination ( ), Common ( , ) SiteSecurity ( , ). : BusinessBinder , SmartTextBox EditableSingleCustomer .

, Utility Helper . - (. AV1008).

AV1709


AV1710

 class Employee { // ! static GetEmployee() {} DeleteEmployee() {} //  static Get() {...} Delete() {...} //  . AddNewJob() {...} RegisterForMeeting() {...} } 

AV1711 , .NET Framework

.NET , .NET Framework. , . , , , , , Add , Remove Count AddItem , Delete , NumberOfItems .

AV1712 ,

, , ,

 bool b001 = (lo == l0) ? (I1 == 11) : (lOl != 101); 

AV1715 Do not be lazy to give appropriate names to the properties.


AV1720 , -

-. – ShowDialog . , , , . And . , , (AV1115).

AV1725 , (), ,

, :

 AvivaSolutions.Commerce.Web NHibernate.Extensibility Microsoft.ServiceModel.WebApi Microsoft.VisualStudio.Debugging FluentAssertion.Primitives CaliburnMicro.Extensions 

: , , , Collections , .

AV1735

. : Click , Deleted , Closing , Minimizing , Arriving . Search , :

 public event EventHandler<SearchArgs> Search; 

AV1737 –ing –ed , -

, , , Closing , , , — Closed . Before After - .

, , . Deleting Deleted , BeginDelete EndDelete . , :


AV1738 On

On , . , Closing , OnClosing .

AV1739 -,

-, , , , , .

 button.Click += (_, __) => HandleClick(); 

AV1745 Extentions

, . Extensions .

AV1755 Async TaskAsync
, Task , — Async . , TaskAsync .


AV1800 Any() , IEnbmerable

IEnumerable , Count , Any() Count() , . Count() , , .. (, IQueryable ).

: IEnumerable , , AV1130, .NET 4.5 , read-only .

AV1820 async

async - , Task.Run . Async , , , . , async , I/O.

AV1825 Task.Run

, , Task.Run , . , .

AV1830 await/async Task.Wait

await , . Task.Wait (. AV1835).

AV1835 async/await
:

 private async Task<string> GetDataAsync() { var result = await MyWebService.GetDataAsync(); return result.ToString(); } 

ASP.NET MVC :

 public ActionResult ActionAsync() { var data = GetDataAsync().Result; return View(data); } 

. Why?Because the getter properties Resultwill block the stream until the operation asyncis completed, but since the method asyncwill automatically return the result to the original stream, and ASP.NET uses a single-threaded synchronization context, they will continue to wait for each other. A similar problem can also occur with WPF, Silverlight or with C # / XAML Windows Store apps. You can learn more about it here .

Recommendations for using the framework


AV2201 Use C # type aliases instead of namespace types. System

For example, use objectinstead of Object, stringinstead of, Stringand intinstead of Int32. These pseudonyms were introduced to make primitive types first-class C # members, so use them properly.

Exception : When referring to static elements of such types, it is usually customary to use the full CLS name, for example, Int32.Parse()instead int.Parse().

AV2205 Carefully specify the names of properties, variables, or fields referencing localized resources. The

recommendations in this section apply to localizable resources, such as error messages and menu text.


AV2207 ,

, , .. , ConnectionStrings ConfigurationManager Settings , Visual Studio. app.config web.config ( - ).

AV2210

, 4 C# «Treat warnings as errors» ( ). .

AV2215 AssemblyInfo.cs

, , , .. . , , , , AssemblyInfo.cs SolutionInfo.cs , .

AV2220 LINQ

Instead:

 var query = from item in items where item.Length > 0; 

System.Linq :

 var query = items.Where(i => i.Length > 0); 

LINQ- . , .

AV2221 -

- . , :

 Customer c = Array.Find(customers, delegate(Customer c) { return c.Name == “Tom”; }); 

-:

 Customer c = Array.Find(customers, c => c.Name == “Tom”); 

:

 var customer = customers.Where(c => c.Name == “Tom”); 

AV2230 dynamic

dynamic . (performance bottleneck), .

dynamic ( Activator ) Type.GetProperty() Type.GetMethod() COM Iterop.

AV2235 async/await Task
C# 5.0 , , , . . , , :

 public Task<Data> GetDataAsync() { return MyWebService.FetchDataAsync() .ContinueWith(t => new Data (t.Result)); } 

:

 public async Task<Data> GetDataAsync() { var result = await MyWebService.FetchDataAsync(); return new Data (result); } 


AV2301

AV2305 public , protected internal

Visual Studio , - . , , , .

AV2306 XML

XML . , / , .

AV2307 MSDN

MSDN, .

: GhostDoc xml .

AV2310

, , , , , .

AV2316 ,

, , . , , , . , , , .

AV2318 ,

Adding ToDo or some other comment code to a block to keep track of the work that needs to be done may seem like a good decision. But in fact, such comments are not needed by anyone. Use a task tracking system such as Team Foundation Server to track flaws.

Recommendations for registration


AV2400 Use general layout rules.


AV2402

 //   Microsoft  using System; using System.Collections; using System.XML; //        using AvivaSolutions.Business; using AvivaSolutions.Standard; using Telerik.WebControls; using Telerik.Ajax; 

AV2406

  1. ( #region )
  2. read-only
  3. Developments

. , , . , .

AV2407 #region

#region , . , #region :




The site of the company


, , C# . CodePlex. www.csharpcodingguidelines.com .

:



useful links


, , , , .

Code Complete: A Practical Handbook of Software Construction (Steve McConnel) . , - . . , 2004, , , , . , 2009.

The Art of Agile Development (James Shore) . , , Scrum . , .

Applying Domain Driven-Design and Patterns: With Examples in C# and .NET (Jimmy Nilsson) . , - (DDD) (TDD). , , .

Jeremy D. Miller's Blog . , , , . , .

LINQ Framework Design Guidelines . , IQueryable.

Best Practices for c# async/await . , . .

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


All Articles