Поради для кращого написання коду мовою C#

21 хв. читання

Вступ та загальні відомості

Я програмую мовою С# вже більше чотирьох років, спостерігаючи, як ця мова змінювалася від С# 5 до С# 6. В цілому, С# вже розвинувся, але більшість програмістів ще не достатньо удосконалили свої навички, щоб зрозуміти основні процеси для своїх проектів, роблячи їх легкими для читання. Головна ідея цієї статті – дозволити Вам писати чудові проекти мовою С#, навіть з відкритим кодом, а також розробляти структуру додатку, який розробляється командою. Таким чином, написання гарного коду - це позитивна риса для розробників, тому що вже написаний код буде використовуватися, управлятися та оновлюватися іншими розробниками, які будуть намагатися слідувати певній філософії та практиці програмування даної команди. У такому випадку, найкращий підхід – якимось чином дотримуватись рекомендацій кодування. Але моя стаття дасть Вам лише огляд того, як вам потрібно додавати дизайн та стиль до Ваших С#-програм, аби зробити їх кращими для читачів.Слід зазначити, що С#-компілятор не додасть вашим програмам стилю, який ви самі вкладаєте у Ваш код, саме продуктивність буде зменшена(або збільшена) за рахунок зміни стилю коду.

Не говорячи вже чого-небудь іншого, давайте зануримося глибше у С#-додатки: таким чином, вони виглядають простішими, добре зробленими та легшими для читачів, в той же час вони зберігають свою продуктивність та ефективність. Отож, є декілька речей, які я б хотів, аби Ви знали на момент читання цієї статті:

  1. Покращення в C#, які були зроблені у 6-тій версії.

  2. LINQ у .NET структурі.

  3. Асинхронне програмування і об'єкт Task в C#.

  4. Небезпечне програмування в C#, яке дозволяє управляти пам'яттю.

Я буду направляти вас декількома способами, за допомогою яких ви зможете зробити Ваші програми на C# набагато кращими, ніж вони були до цього, коли Ви ще не читали цю статтю. Я також постараюся зробити так, щоб не йти проти будь-який ваших правил кодування та проти правил діяльності у Ваших командах.

Не говоримо про продуктивність

Слід зазначити, що я не говорю про зміну продуктивності програми, про підвищення ефективності, про скорочення часу, використаний програмою для запуску. Написавши хороший С#-код, можна поліпшити продуктивність в лічені секунди, але не всі поради гарантують, що ваш код буде виконуватись краще. Це значить, що важливо не те, ЯК ви написали код, а те, ЯКИЙ код Ви написали. Це говорить про те, що код стане більш стійким до переривання під час його виконання, особливо тоді, коли це не очікується.

Навіщо писати чистий код?

Ви пишете код, компілятор компілює без будь-яких попереджень і без помилок, маємо результат: код прекрасний. Але що робити, якщо хтось хотів прочитати цей код і для нього він виявився незрозумілим? Що робити, якщо хтось захоче модернізувати Ваш код для вас або компанії, у якій ви працюєте. Давайте подивимося на наступний код:

public static void Main(string[] args)
 {
  int x = 0;
  x = Console.Read();
  Console.WriteLine(x * 1.5);
 }

Ця програма працює добре, ніяких помилок. Але чи можете ви мені сказати, що робить ця програма у реальному житті? Є багато припущень:

  1. Виконує операцію множення.

  2. Збільшує значення числа.

  3. Це також може бути відсотковий коефіцієнт загальної суми рахунку у банку певної людини.

  4. І так далі. І так далі. І так далі.

Який з них більш реальний? Ніхто не буде знати, в яких випадках краще писати хороший код та слідувати основам програмування. Подивімося на наступний код:

public static void Main(string[] args)
 {
  int salary = 0;
  salary = Console.Read();
  Console.WriteLine(salary * 1.5);
 }

У цій статті я не буду показувати Вам, як дотримуватися найкращих принципів. Замість цього я почну розширювати ваші знання, навчу вас, як зробити Ваші програми на C# кращими. Я буду вчити Вас, як використовувати C#-логіку у ваших додатках, заради багатьох переваг, які ви можете отримати від додатків, шляхом написання програм у певній манері та з певною структурою. Отож, давайте почнемо.

Рахуйте усі поради…

Це не займе багато часу, але приділіть трохи уваги, щоб дізнатися головні правила для покращення ваших програм на C#.

Ініціалізація об'єкта

C# є об'єктно-орієнтованою мовою програмування. У цьому розділі будемо говорити про декілька речей, які ви повинні вже знати, перш ніж рухатися вперед до написання new Object() коду для додатків. Ви повинні знати про те, як створюються класи в C#, як певні речі співпрацюють, щоб викликати невелику програму у вашій системі. Наприклад, погляньмо на наступний код:

class Person {
  public int ID { get; set; }
  public string Name { get; set; }
  public DateTime DateOfBirth { get; set; }
  public bool Gender { get; set; }
}

Ви можете створювати програми, які встановлюють значення поля за замовчуванням, або встановлюються з будь-яких баз даних, орієнтованих на джерело даних, як цей вихідний код, це спростить Вам життя: значення за замовчуванням в об'єкті створяться у момент створення цього об'єкту.

var person = new Person { ID = 1, Name = "Afzaal Ahmad Zeeshan", DateOfBirth = new DateTime(1995, 08, 29), Gender = true };

Також це можна зробити наступним чином:

var person = new Person();
 person.ID = 1;
 person.Name = "Afzaal Ahmad Zeeshan";
 // і так далі.

Там немає помітного поліпшення продуктивності коду тут, але читабельність коду дійсно стала кращою. Якщо ви любите робити відступи, погляньте на наступний код:

var person = new Person
             {
                ID = 1,
                Name = "Afzaal Ahmad Zeeshan",
                DateOfBirth = new DateTime(1995, 08, 29),
                Gender = true
             };

Який код має відступи, то це додає трохи більше роз'яснень. Незважаючи на те, ці коди роблять одне і те саме, але тут код стає більш читабельним та лаконічним.

NULL-перевірка

Ви коли-небудь були роздратованими NullReferenceException? Коли об'єкт, який пропустив ініціалізацію повертається для «помсти»? Є багато переваг використання NULL-перевірок у своїх програмах, не тільки для кращого читання, але і в разі забезпечення того, щоб програма не закінчувалася через проблеми, пов'язані з пам'яттю, наприклад, коли змінна не існує в пам'яті. Вони можуть також додати безпеки вашим програмам. У більшості випадків, NullReferenceException виникає у зв'язку з цим:

 string name = null;
  Console.WriteLine(name);

У більшості випадків, сам компілятор не буде працювати до тих пір, поки це виправиться само собою, хіба що ви якимось чином обдурите компілятор, зробите так, аби він думав, що змінна отримала значення, але на час виконання значення ніякого не було. Null-посилання тут приходять на допомогу. Щоб подолати цю помилку, ви можете просто зробити наступне:

string name = null; // Спробуйте ввести значення звідки хочете
 if(name != null)
 {
  Console.WriteLine(name);
 }

Цей безпечний спосіб гарантуватиме, що значення було доступне, коли ця змінна була викликана. В іншому випадку, уся програма буде рухатися в іншому напрямку. У C# 6 є ще один спосіб подолання цієї помилки. Розглянемо сценарій, де ваша база даних була встановлена, ваша таблиця даних була створена, ваша особа була знайдена, але деталей зайнятості знайдено не було. Чи можете ви отримати відомості про компанію, де працює ця людина?

var company = DbHelper.PeopleTable.Find(x => x.id == id).FirstOrDefault().EmploymentHistory.CompanyName; // Помилка

Якщо ви зробите так, то це буде помилка, тому що ми зможемо пройти лише декілька рядків такого коду. Тоді ми в кінці отримаємо нульове значення і все буде втрачено. У C# 6 приходить новий спосіб подолання таких проблем, за допомогою безпечної навігації оператора після значень і полів, які можуть бути нульовими ; ?.., ось як ці.

var company = DbHelper?.PeopleTable?.Find(x => x.id == id)?.FirstOrDefault()?.EmploymentHistory?.CompanyName; // Працює

Цей код буде робити таке: він буде перевіряти для наступного значення, чи попереднє не було нульовим. Якщо попереднє значення дорівнювало нулю, то він поверне нуль і збереже нуль як значення для компанії. Замість того, щоб кидати помилку, це може стати в нагоді, коли ви залишите перевірки в самій структурі . Але, тим не менше, ви все одно повинні перевірити в кінці, чи інші значення рівні нулю чи ні.

var company = DbHelper.PeopleTable?.Find(x => x.id == id)?.FirstOrDefault()?.EmploymentHistory?.CompanyName;
if(company != null)
{ 
 // Кінцевий процес
}

Але замість того щоб писати код і перевіряти всі змінні на ненульові, ви можете зробити просту перевірку. Іншими словами, потрібно буде використати try...catch або декілька if...else блоків, щоб контролювати , як ваша програма переходить по системі.

Асинхронна модель програмування

Якщо ви програмуєте за мовою C# 5, то ви вже автоматично використовуєте асинхронну модель програмування, воно якимось чином відобразиться на ваших програмах, але якщо це не так, то я б рекомендував використовувати асинхронний шаблон програмування до коду програми. Це не тільки може вплинути на програму, але також підвищить читабельність вашого додатку. Далі наведено деякі переваги використання асинхронної моделі у вашому коді:

  1. Зміст програми набуває більше сенсу. Програмісти починають розуміти, як буде працювати програма на задньому плані, тобто на рівні машинного коду.

  2. Проблема зависання додатків перестане існувати, більшість проблем так званого «заморожування» додатків надходить від коду, де потік інтерфейсу користувача не може себе оновити, тоді користувачі вважають, що додаток завис і не відповідає. Тут на допомогу прийде асинхронний підхід.

  3. Додатки Windows повністю засновані на такому підході. Ви повинні (обов'язково!) використовувати цей підхід, створюючи Windows-додатки, аби подолати проблеми зависання програм та поганий стиль програмування. З тих пір патерне проектування коду і почало існувати, асинхронність стала життєво-необхідною частиною програм і додатків, саме тому, ви повинні користуватися цією функцією у вашому коді.

Рядкові форми у С#

Рядки є життєво-важливою частиною додатків у наш час і сворення рядків може зайняти багато часу, а іноді може викликати відставання продуктивності в додатках. Є багато способів, якими ви можете створити рядки в програмах на C#. Нижче наведені деякі з способів:

string str = ""; // Встановивши null створяться додаткові проблеми.
// Перший шлях
str = "Name: " + name + ", Age: " + age;
// Другий шлях
str = string.Format("Name: {0}, Age: {1}", name, age);
// Третій шлях
var builder = new StringBuilder();
builder.Append("Name: ");
builder.Append(name);
builder.Append(", Age: ");
builder.Append(age);
str = builder.ToString();

Зверніть увагу на те, що рядки в C# незмінні. Це означає, що якщо ви спробуєте змінити їхні значення, то значення буде замінено безповоротно, тобто попереднє значення видалиться з пам'яті. Тому перший спосіб здається найкращим, але це не так. Найкращий спосіб – це третій спосіб, який дозволяє створювати рядки без оновлень їх у пам'яті. Проте, C# 6 представив абсолютно новий спосіб побудови рядків у C# і це набагато кращий спосіб, який ви тільки можете собі уявити. Новий оператор інтерполяції (String interpolation operator $ ) дозволяє якнайкраще побудувати рядки. Дана операція виглядає наступним чином:

static void Main(string[] args)
{
// Тільки довільні змінні
string name = "";
int age = 0;

// Наші варіанти
string str = $"Name: {name}, Age: {age}";
}

Лише один рядок коду, і компілятор автоматично перекомпілює дані символи до версії string.Format(). Для доказу, я збираюся прочитати бай -код, який згенерувала дана C#-програма, і вона покаже вам, як зміниться синтаксис програми, коли відбуватиметься форматоване читання рядків.

IL_0000: nop
IL_0001: ldstr ""
IL_0006: stloc.0     // ім'я
IL_0007: ldc.i4.0
IL_0008: stloc.1     // вік
IL_0009: ldstr "Name: {0}, Age: {1}"
IL_000E: ldloc.0     // ім'я
IL_000F: ldloc.1     // вік
IL_0010: box         System.Int32
IL_0015: call        System.String.Format
IL_001A: stloc.2     // рядок
IL_001B: ret

Тут можна побачити, як змінюється синтаксис. Подивіться на IL_0009. Крім того, ваш код стає «чистішим», на нього приємніше дивитись, читаючи його, в той же час він стає більш продуктивним, якщо рядки, які будуються є невеликими.

Використання циклів з великою кількістю даних

Що хорошого може зробити додаток, у якому будуть відсутні цикли та ітерації? Деколи Вам потрібно буде знайти значення змінної, якийсь вузол, запис, або здійснити будь-який інший обхід даних. У такому випадку ви повинні бути точно впевненим, що пишете правильний код, оскільки це та область, де продуктивність і читабельність відіграє важливу роль і вони є тісно пов'язаними між собою. Я робив це сам і з невеликим досвідом я зміг подолати неправильний спосіб написання коду для читання і обходу даних. Тобто саме тут Вам допоможе досвід з LINQ, це дозволить писати програми, які використовують .NET-фреймворки, це забезпечить досвід кодування і додасть досвіду для користувачів і клієнтів.

Раніше ви могли вже таке робити:

// Функція пошуку людей
Person FindPerson(int id) {
var people = DbContext.GetPeople(); // Повертає List<person>

foreach (var person in people) {
    if(person.ID == id) {
        return person;
    }
 }

// Ні одну людину не знайдено
return null;
}

// Далі виконується таке
var person = FindPerson(123);

Це легко читається тими, хто намагається увійти у вашу сферу роботи. Але тим не менше, код буде простішим та чистішим за допомогою LINQ запитів у C#. Є два способи, якими ви можете зробити це. Одним з них схожий на SQL, а інший використовує функцію Where, передаючи вимоги, які ми вже маємо. Давайте подивимося на обидва способи.

// Функція пошуку людей
Person FindPerson(int id)
{
  var people = DbContext.GetPeople(); // Повертає List<person>
  return (from person in people
  where person.ID == id
  select person).ToList().FirstOrDefault();
} 

// Далі виконується таке
var person = FindPerson(123);

Цей код має SQL-подібний зовнішній вигляд,саме тому підвищить читабельність і продуктивність вашого коду. Функція аналогічна, проте коди читається трохи краще, залишаються ітерації - орієнтовані на .NET-фреймворки. .NET робитиме все можливе, щоб забезпечити найкращу продуктивність для додатків. Тепер давайте розглянемо інший спосіб написання цього запиту в коді на C#:

// Функція пошуку людей
Person FindPerson(int id) {
  var people = DbContext.GetPeople(); // Повертає List<person>
  return people.Where(x => x.ID == id).FirstOrDefault();
}

// Далі виконується таке
var person = FindPerson(123);

Зверніть увагу на те, що перший код повертає null в разі, якщо не було знайдено збіг. Ці коди також роблять те ж саме. Єдине, що код виконує погано це те, що він повинен був виконувати ітерації над самою колекцією. Відбудеться повернення return person; Складність цього алгоритму пошуку даних буде O(N).

Уникайте небезпечного контексту

C# також підтримує ручне управління пам'яттю, це те ж саме, коли ви повинні бути пілотом свого ж літака! Небезпечний контекст в C # дозволяє маніпулювати пам'яттю, виконувати арифметичні операції над покажчиками, зчитувати і записувати дані в осередках пам'яті, до яких немає доступу і так далі. Проте, структура .NET робить багато речей, щоб подолати проблеми з пам'яттю, затримку та інші речі. Це також дозволяє .NET подолати необхідність фактично здійснювати будь-яке управління пам'яттю взагалі, сам .NET-фреймворк робитиме це за вас. В даний час існує багато переваг використання небезпечного контексту, наприклад, коли ви хочете написати wrappers-класи у власних бібліотеках C++. Emgu CV є одним з таких прикладів, де ви можете написати деякий код, який обробляє його початковий варіант коду і обробляє помилки в пам'яті так, аби вони були простішими. У цьому контексті ви можете зробити так:

  1. Управління покажчиками і арифметичні операції над покажчиками. Ви не можете виконати що-небудь з адресами поза цим контекстом, тобто де існують правила .NET.

  2. Управління пам'яттю, управляти об'єктами прямо в пам'яті.

  3. Використання C++ стилю програмування; саме під такий стиль C# був розроблений.

Таким чином, немає ніяких переваг або меншої вигоди, і якщо ви будете застосовувати це в своїх додатках, застосовуйте їх з розумом.

Все, що було сказано про unsafe було особисто думкою

Я також хочу повідомити, що все, що я сказав, і плюси, і мінуси, це я розділив мої особисті погляди, я не часто використовую unsafe у своїх програмах, тому що немає ніяких підстав, навіщо я повинен використовувати його в своїх програмах. Проте, якщо ваші програми вимагають управління пам'яттю, то ви можете використовувати цей контекст.

Використовуйте лямбда-вирази, де це можливо

Лямбда приходять з функціонального програмування, так і в C# вона широко використовується у багатьох відношеннях, починаючи від вбудованих функцій аж до гетерних властивостей у С#. Я покажу обидва цих використань в C#, що робить програму не тільки кращою, але така програма має кращий коефіцієнт продуктивності; для цього я покажу вам IL цього C#-коду. Я особисто люблю використовувати лямбда в багатьох областях, особливо, де я повинен написати вбудовані функції в C#. З тих пір як лише гетерні властивості можуть бути написані за допомогою цієї концепції, я використовую їх і особисто кажу, що це краще, ніж попередні способи зробити одне і те ж саме.

1. Використання лямбда для вбудованих функцій

Ви повинні добре знати, що таке делегати у C# програмуванні. Є багато областей, де вони використовуються, наприклад, у випадках обробки подій в додатках. Для обробки подій, ви можете написати свої поточні функції, наприклад:

// Без лямбда
myBtn.Click += Btn_Click;
public void Btn_Click (object sender, EventArgs e) {
  // Код затримує подію
 }

// З використанням лямбда
myBtn.Click += (sender, e) =>
 {
 // Код затримує подію
 }

Зверніть увагу, що компілятор буде автоматично зіставляти об'єкти з їх типами. Це зручно у багатьох випадках, це дозволяє записувати вбудовані функції в C#, які залишаються з об'єктами до тих пір, поки ви не захочете використовувати їх в іншому місці. Існує, однак, один недолік цього методу обробки подій, недоліком є те, що ви не можете видалити обробник подій, як тільки він був встановлений. У C # ви можете, але так як у нас немає будь-яких посилань, щоб видалити події, ми залишимо тільки використовувати окремі функції. Але, якщо вам не потрібно видалити обробники, ви завжди повинні розглянути питання про використання обробників подій у ваших програмах.

2. Використання лямбда для getter-only властивостей

У C# є поняття використання властивостей замість полів. Де ви контролюєте, як встановлено значення, і як значення отримується з поля. Розглянемо альтернативу (або дещо аналогічне) до гетера і сетера у мові програмування Java. Різниця лише в тому, що вам не доведеться писати їх окремо десь, вони написані прямо на передньому плані самого поля. Створювачі C# потім зробили свої власні поля акомпанементу, які використовуються для зберігання значень. В принципі, ви повинні писати getter-only, подібно цьому:

public string Name { get; }

Зверніть увагу, що ці властивості є постійними і ви не можете змінити їх, як тільки вони були встановлені. Вони були встановлені в конструкторах, або вказані прямо, як у наступному прикладі:

public string Name { get; } = "Afzaal Ahmad Zeeshan";

Однак, так як ми вже знаємо, що це константне поле, і ви не можете змінити його, то чому б не створити просту константну властивість? Це де все стає трохи складніше, навіть властивість має бути підкріплено полем. У цьому випадку можна зробити так:

public string Name => "Afzaal Ahmad Zeeshan";

Або це рівносильно наступному: public string Name { get { return "Afzaal Ahmad Zeeshan"; } } Але продуктивність набагато краща, коли поле-геттер перетворюється в константне поле, а потім поле використовується в програмі в будь-який час, коли його було викликано.

Заключні слова

Це стаття, де ви можете знайти кілька порад, які можуть допомогти вам у створення більшості додатків на C# з використанням найкращих способів написання програм на C#, де продуктивність може бути також підвищена. Якщо у вас є більше порад, дайте мені знати, я хотів би почути їх. :-)

Метою даної статті було те, щоб дозволити вам, хлопці, щоб зрозуміти і побачити деякі з способів, з якими ваші програми можуть бути кращими в читанні і виконанні. C# компілятор сам підвищує якість коду та його ефективність, тоді як іноді все залежить від волі програміста, щоб зробити роботу програми кращою потрібна і його робота. Є багато інших способів поліпшення читабельності, іноді деякі з них компанія впроваджує сама, тому що більшість команд вимагають програмістів слідувати їх власним методам програмування. Стаття також розглядає ці стандарти і надає способи, в яких ви можете написати хороший код і надалі дотримуватися цих принципів.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 6.2K
Приєднався: 7 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація