socialgekon.com
  • Основен
  • Разпределени Екипи
  • Публикуване
  • Управление На Проекти
  • Мобилен Дизайн
Технология

Код на бъги C #: 10-те най-често срещани грешки в програмирането на C #

Относно C Sharp

° С # е един от няколкото езика, насочени към Microsoft Общоезично изпълнение (CLR). Езиците, насочени към CLR, се възползват от функции като междуезична интеграция и обработка на изключения, подобрена сигурност, опростен модел за взаимодействие с компоненти и услуги за отстраняване на грешки и профилиране. От днешните CLR езици C # е най-широко използваният за сложни, професионални проекти за развитие които са насочени към средите за настолни компютри, мобилни устройства или сървъри на Windows.

C # е обектно ориентиран, силно типизиран език. Строгата проверка на типа в C #, както по време на компилация, така и по време на изпълнение, води до това, че повечето типични грешки при програмиране на C # се отчитат възможно най-рано и техните местоположения се определят доста точно. Това може да спести много време в C Остро програмиране , в сравнение с проследяването на причината за озадачаващи грешки, които могат да възникнат дълго след извършване на нарушителната операция на езици, които са по-либерални с прилагането на безопасността на типа. Въпреки това, много C # кодери неволно (или небрежно) изхвърлят предимствата на това откриване, което води до някои от проблемите, обсъдени в този урок за C #.

За този урок за програмиране на C Sharp

Този урок описва 10 от най-честите грешки при програмиране на C # или проблеми, които трябва да се избягват, от програмистите на C # и им предоставя помощ.



Въпреки че повечето от грешките, обсъдени в тази статия, са специфични за C #, някои са подходящи и за други езици, които са насочени към CLR или използват Библиотека на Framework Class (FCL).

Често срещана грешка в програмирането на C # # 1: Използване на препратка като стойност или обратно

Програмистите на C ++ и много други езици са свикнали да контролират дали стойностите, които те присвояват на променливи, са просто стойности или са препратки към съществуващи обекти. При програмирането на C Sharp обаче това решение се взема от програмиста, който е написал обекта, а не от програмиста, който създава екземпляр на обекта и го присвоява на променлива. Това е често срещано понятие за тези, които се опитват да научат програмиране на C #.

Ако не знаете дали обектът, който използвате, е тип стойност или референтен тип, може да срещнете някои изненади. Например:

Point point1 = new Point(20, 30); Point point2 = point1; point2.X = 50; Console.WriteLine(point1.X); // 20 (does this surprise you?) Console.WriteLine(point2.X); // 50 Pen pen1 = new Pen(Color.Black); Pen pen2 = pen1; pen2.Color = Color.Blue; Console.WriteLine(pen1.Color); // Blue (or does this surprise you?) Console.WriteLine(pen2.Color); // Blue

Както можете да видите, и Point и Pen обектите са създадени по същия начин, но стойността на point1 остана непроменен, когато нов X координатна стойност беше присвоена на point2, докато стойността на pen1 беше модифициран, когато е зададен нов цвят на pen2. Следователно можем извеждам че point1 и point2 всеки съдържа свое копие на Point обект, докато pen1 и pen2 съдържат препратки към същото Pen обект. Но как можем да знаем това, без да правим този експеримент?

Отговорът е да разгледате дефинициите на типовете обекти (което можете лесно да направите в Visual Studio, като поставите курсора върху името на типа обект и натиснете F12):

public struct Point { ... } // defines a “value” type public class Pen { ... } // defines a “reference” type

Както е показано по-горе, при програмиране на C #, struct ключовата дума се използва за дефиниране на тип стойност, докато class ключова дума се използва за дефиниране на референтен тип. За тези с C ++ фон, които са приспити във фалшиво чувство за сигурност от многото прилики между ключовите думи C ++ и C #, това поведение вероятно е изненада, поради която може да поискате помощ от урок за C #.

Ако ще зависи от някакво поведение, което се различава между стойностите и референтните типове - като например способността да се предава обект като параметър на метод и този метод да променя състоянието на обекта - уверете се, че имате работа с правилен тип обект, за да се избегнат проблеми с програмирането на C #.

Често срещана грешка в програмирането на C # # 2: Неразбиране на стойностите по подразбиране за неинициализирани променливи

В C # типовете стойности не могат да бъдат нула. По дефиниция типовете стойности имат стойност и дори неинициализираните променливи на типовете стойности трябва да имат стойност. Това се нарича стойност по подразбиране за този тип. Това води до следния, обикновено неочакван резултат при проверка дали дадена променлива е неинициализирана:

class Program { static Point point1; static Pen pen1; static void Main(string[] args) { Console.WriteLine(pen1 == null); // True Console.WriteLine(point1 == null); // False (huh?) } }

Защо не е point1 нула? Отговорът е, че Point е тип стойност и стойността по подразбиране за Point е (0,0), не е нула. Неразпознаването на това е много лесна (и често срещана) грешка в C #.

Много (но не всички) типове стойности имат IsEmpty свойство, което можете да проверите дали е равно на стойността му по подразбиране:

Console.WriteLine(point1.IsEmpty); // True

Когато проверявате дали дадена променлива е инициализирана или не, уверете се, че знаете каква стойност ще има неинициализирана променлива от този тип по подразбиране и не разчитайте тя да е нула.

Често срещана грешка в програмирането на C # # 3: Използване на неправилни или неуточнени методи за сравнение на низове

Има много различни начини за сравняване на низове в C #.

Въпреки че много програмисти използват == оператор за сравнение на низове, той всъщност е един от най-малко желани методи за използване, главно защото не посочва изрично в кода кой тип сравнение се иска.

По-скоро предпочитаният начин за тестване на равенство на низове в програмирането на C # е с Equals метод:

public bool Equals(string value); public bool Equals(string value, StringComparison comparisonType);

Първият подпис на метода (т.е. без параметъра comparisonType) всъщност е същият като използването на == оператор, но има предимството да бъде изрично приложен към низове. Той извършва поредно сравнение на низовете, което по същество е байта по байт сравнение. В много случаи това е точно типът на сравнението, който искате, особено когато сравнявате низове, чиито стойности са зададени програмно, като имена на файлове, променливи на околната среда, атрибути и др. В тези случаи, докато редовото сравнение наистина е правилният тип за сравнение за тази ситуация, единственият недостатък на използването на Equals метод без comparisonType е, че някой, който чете кода, може да не знае какъв тип сравнение правите.

Използване на Equals подпис на метод, който включва comparisonType всеки път, когато сравнявате низове, обаче, не само ще направите вашия код по-ясен, но ще ви накара да мислите изрично кой тип сравнение трябва да направите. Това си струва да се направи, защото дори ако английският език не може да осигури много разлики между сравненията между редовни и културно чувствителни, други езици предоставят изобилие и пренебрегването на възможността за други езици се отваря за много потенциал за грешки по пътя. Например:

string s = 'strasse'; // outputs False: Console.WriteLine(s == 'straße'); Console.WriteLine(s.Equals('straße')); Console.WriteLine(s.Equals('straße', StringComparison.Ordinal)); Console.WriteLine(s.Equals('Straße', StringComparison.CurrentCulture)); Console.WriteLine(s.Equals('straße', StringComparison.OrdinalIgnoreCase)); // outputs True: Console.WriteLine(s.Equals('straße', StringComparison.CurrentCulture)); Console.WriteLine(s.Equals('Straße', StringComparison.CurrentCultureIgnoreCase));

Най-безопасната практика е винаги да предоставяте comparisonType параметър към Equals метод. Ето някои основни насоки:

  • Когато сравнявате низове, въведени от потребителя или които трябва да бъдат показани на потребителя, използвайте сравнение, чувствително към културата (CurrentCulture или CurrentCultureIgnoreCase).
  • Когато сравнявате програмни низове, използвайте редови сравнения (Ordinal или OrdinalIgnoreCase).
  • InvariantCulture и InvariantCultureIgnoreCase обикновено не се използват, освен при много ограничени обстоятелства, защото редовите сравнения са по-ефективни. Ако е необходимо сравнение, съобразено с културата, то обикновено трябва да се извърши спрямо настоящата култура или друга специфична култура.

В допълнение към Equals метод, низовете също предоставят Compare метод, който ви дава информация за относителния ред на низовете, вместо просто тест за равенство. Този метод е за предпочитане пред <, <=, > и >= оператори, по същите причини, както обсъдени по-горе - за да се избегнат проблеми с C #.

Свързани: 12 основни .NET интервюта

Често срещана грешка в програмирането на C # # 4: Използване на итеративни (вместо декларативни) изрази за манипулиране на колекции

В C # 3.0 добавянето на Езиково интегрирана заявка (LINQ) на езика, променен завинаги начинът, по който колекциите се заявяват и манипулират. Оттогава, ако използвате итеративни изрази за манипулиране на колекции, не сте използвали LINQ, когато вероятно е трябвало.

Някои програмисти на C # дори не знаят за съществуването на LINQ, но за щастие този брой става все по-малък. Мнозина все още мислят, че поради сходството между ключовите думи LINQ и SQL изрази, единственото му използване е в код, който заявява бази данни.

Докато заявките за бази данни са много разпространено използване на оператори LINQ, те всъщност работят над всякакви изброени колекции (т.е. всеки обект, който реализира интерфейса IEnumerable). Така например, ако сте имали масив от акаунти, вместо да пишете C # Списък на foreach:

decimal total = 0; foreach (Account account in myAccounts) { if (account.Status == 'active') { total += account.Balance; } }

можете просто да напишете:

decimal total = (from account in myAccounts where account.Status == 'active' select account.Balance).Sum();

Въпреки че това е доста прост пример за това как да се избегне този често срещан проблем с програмирането на C #, има случаи, когато един оператор LINQ може лесно да замени десетки изрази в итеративен цикъл (или вложени цикли) във вашия код. И по-малко общ код означава по-малко възможности за бъгове да бъдат въведени. Имайте предвид обаче, че може да има компромис по отношение на ефективността. В критични за производителността сценарии, особено когато вашият итеративен код е в състояние да направи предположения за вашата колекция, които LINQ не може, не забравяйте да направите сравнение на производителността между двата метода.

Често срещана грешка при програмиране на C # # 5: Неразглеждане на основните обекти в оператор LINQ

LINQ е чудесен за абстрахиране на задачата за манипулиране на колекции, независимо дали са обекти в паметта, таблици на база данни или XML документи. В един перфектен свят не би трябвало да знаете какви са основните обекти. Но грешката тук предполага, че живеем в перфектен свят. Всъщност идентичните оператори LINQ могат да връщат различни резултати, когато се изпълняват върху същите данни, ако тези данни са в различен формат.

Например, помислете за следното твърдение:

decimal total = (from account in myAccounts where account.Status == 'active' select account.Balance).Sum();

Какво се случва, ако един от обекта е account.Status е равно на „Активен“ (обърнете внимание на главното A)? Е, ако myAccounts беше DbSet обект (който е настроен с конфигурацията по подразбиране, нечувствителна на малки и малки букви), where израз все още ще съответства на този елемент. Ако обаче myAccounts е в масив в паметта, той няма да съвпадне и следователно ще доведе до различен резултат за total.

Но почакай малко. Когато говорихме за сравнение на низове по-рано, видяхме, че == оператор извърши поредно сравнение на низове. И така, защо в този случай е == оператор, извършващ сравнение без регистър?

Отговорът е, че когато основните обекти в оператор LINQ са препратки към данни от таблица на SQL (какъвто е случаят с обекта на Entity Framework DbSet в този пример), изразът се преобразува в T-SQL израз. След това операторите следват правилата за програмиране на T-SQL, а не правилата за програмиране на C #, така че сравнението в горния случай се оказва нечувствително към регистъра.

Като цяло, въпреки че LINQ е полезен и последователен начин за заявки за колекции от обекти, в действителност все още трябва да знаете дали вашето изявление ще бъде преведено в нещо различно от C # под капака, за да се гарантира, че поведението на вашия код ще да бъде както се очаква по време на изпълнение.

Често срещана грешка в програмирането на C # # 6: Объркване или фалшифициране чрез методи за разширение

Както бе споменато по-рано, операторите LINQ работят върху всеки обект, който реализира IEnumerable. Например следната проста функция ще събере салдата по всяко събиране на сметки:

public decimal SumAccounts(IEnumerable myAccounts) { return myAccounts.Sum(a => a.Balance); }

В горния код типът на параметъра myAccounts се декларира като IEnumerable. Тъй като myAccounts препратки a Sum метод (C # използва познатата „точкова нотация“ за препратка към метод в клас или интерфейс), бихме очаквали да видим метод, наречен Sum() относно дефиницията на IEnumerable интерфейс. Определението на IEnumerable обаче не прави препратка към Sum метод и просто изглежда така:

public interface IEnumerable : IEnumerable { IEnumerator GetEnumerator(); }

И така, къде е Sum() дефиниран метод? C # е силно написан, така че ако препратката към Sum методът е невалиден, компилаторът C # със сигурност ще го маркира като грешка. Следователно знаем, че трябва да съществува, но къде? Освен това, къде са дефинициите на всички останали методи, които LINQ предоставя за заявки или агрегиране на тези колекции?

Отговорът е, че Sum() не е метод, дефиниран на IEnumerable интерфейс. По-скоро това е статичен метод (наречен „метод на разширение“), който е дефиниран в System.Linq.Enumerable клас:

namespace System.Linq { public static class Enumerable { ... // the reference here to “this IEnumerable source” is // the magic sauce that provides access to the extension method Sum public static decimal Sum(this IEnumerable source, Func selector); ... } }

И така, какво прави метода на разширението различен от всеки друг статичен метод и какво ни дава достъп до него в други класове?

Отличителната характеристика на метода за удължаване е this модификатор на първия си параметър. Това е „магията“, която го идентифицира на компилатора като метод за разширение. Типът на параметъра, който той модифицира (в случая IEnumerable), обозначава класа или интерфейса, които след това ще се появят за изпълнение на този метод.

(Като странична точка няма нищо магическо в сходството между името на интерфейса IEnumerable и името на класа Enumerable, за който е дефиниран методът на разширение. Това сходство е просто произволен стилистичен избор .)

С това разбиране можем също да видим, че sumAccounts функцията, която въведохме по-горе, можеше вместо това да бъде реализирана, както следва:

public decimal SumAccounts(IEnumerable myAccounts) { return Enumerable.Sum(myAccounts, a => a.Balance); }

Фактът, че вместо това бихме могли да го приложим, повдига въпроса защо изобщо има методи за удължаване? Методи за удължаване са по същество удобство на езика за програмиране C #, който ви позволява да „добавяте“ методи към съществуващи типове, без да създавате нов производен тип, да прекомпилирате или модифицирате по друг начин оригиналния тип.

Методите за удължаване се включват в обхвата, като се включва using [namespace]; изявление в горната част на файла. Трябва да знаете кое пространство на имена на C # включва методите за разширение, които търсите, но това е доста лесно да се определи, след като разберете какво търсите.

Когато компилаторът C # срещне извикване на метод на екземпляр на обект и не намери този метод, дефиниран в реферирания обект клас, той разглежда всички методи на разширение, които са в обхвата, за да се опита да намери такъв, който съответства на необходимия метод подпис и клас. Ако го намери, той ще предаде референцията на екземпляра като първи аргумент към този метод на разширение, а след това останалите аргументи, ако има такива, ще бъдат предадени като следващи аргументи на метода на разширението. (Ако компилаторът C # не намери съответния метод за разширение в обхвата, ще изведе грешка.)

Методите за удължаване са пример за „синтактична захар“ от страна на компилатора C #, което ни позволява да напишем код, който е (обикновено) по-ясен и по-поддържаем. По-ясно, тоест, ако сте наясно с тяхното използване. В противен случай може да бъде малко объркващо, особено в началото.

Въпреки че със сигурност има предимства при използването на методи за удължаване, те могат да създадат проблеми и да помогнат за програмирането на C # за разработчиците, които не са наясно с тях или не ги разбират правилно. Това е особено вярно, когато разглеждате примерни кодове онлайн или друг предварително написан код. Когато такъв код създава грешки в компилатора (тъй като извиква методи, които очевидно не са дефинирани за класовете, към които са извикани), тенденцията е да се мисли, че кодът се прилага за различна версия на библиотеката или изобщо за различна библиотека. Много време може да бъде прекарано в търсене на нова версия или фантомно „липсваща библиотека“, която не съществува.

Дори разработчиците, които са запознати с методите за разширение, все още се хващат от време на време, когато на обекта има метод със същото име, но неговият подпис на метода се различава по фин начин от този на метода на разширението. Много време може да се загуби в търсене на печатна грешка или грешка, която просто я няма.

Използването на методи за разширение в библиотеките на C # става все по-разпространено. В допълнение към LINQ, Unity Application Block и Рамка за уеб API са примери за две широко използвани съвременни библиотеки от Microsoft, които също използват методи за разширение, а има и много други. Колкото по-модерна е рамката, толкова по-вероятно е тя да включва методи за удължаване.

Разбира се, можете да напишете и свои собствени методи за удължаване. Осъзнайте обаче, че докато методите за разширение изглеждат извикани точно като обикновените методи на екземпляр, това всъщност е само илюзия. По-специално, вашите методи за разширение не могат да препращат частни или защитени членове на класа, който разширяват, и следователно не могат да служат като пълен заместител на по-традиционното наследяване на класа.

Често срещана грешка в програмирането на C # # 7: Използване на грешен тип колекция за разглежданата задача

C # предоставя голямо разнообразие от обекти за колекция, като следното е само частичен списък:
Array, ArrayList, BitArray, BitVector32, Dictionary, HashTable, HybridDictionary, List, NameValueCollection, OrderedDictionary, Queue, Queue, SortedList, Stack, Stack, StringCollection, StringDictionary.

Въпреки че може да има случаи, при които твърде много възможности за избор са толкова лоши, колкото и недостатъчен избор, това не е така при обектите за събиране. Броят на наличните опции определено може да работи във ваша полза. Отделете малко повече време предварително, за да проучите и изберете оптималния тип колекция за вашата цел. Това вероятно ще доведе до по-добра производителност и по-малко място за грешки.

Ако има тип колекция, специално насочен към типа на елемента, който имате (като низ или бит), наклонете се първо да използвате този. Внедряването обикновено е по-ефективно, когато е насочено към определен тип елемент.

За да се възползвате от типовата безопасност на C #, обикновено трябва да предпочитате родов интерфейс пред не родов. Елементите на общ интерфейс са от типа, който посочвате, когато декларирате обекта си, докато елементите на не-родови интерфейси са от тип обект. Когато използвате негенеричен интерфейс, компилаторът C # не може да провери вашия код. Също така, когато се занимавате с колекции от примитивни типове стойности, използването на негенерична колекция ще доведе до повторение бокс / разопаковане от тези типове, което може да доведе до значително отрицателно въздействие върху ефективността в сравнение с родова колекция от съответния тип.

Друг често срещан проблем на C # е да напишете свой собствен обект за събиране. Това не означава, че никога не е подходящо, но с толкова изчерпателна селекция, каквато предлага .NET, вероятно можете да спестите много време, като използвате или разширите такава, която вече съществува, вместо да преоткривате колелото. По-специално, C5 Generic Collection Library за C # и CLI предлага широк набор от допълнителни колекции „извън кутията“, като постоянни дървесни структури от данни, приоритетни опашки, базирани на купчина, индексирани списъци с масиви, свързани списъци и много други.

Често срещана грешка в програмирането на C # # 8: Пренебрегване на безплатните ресурси

Средата CLR използва събирач на боклук, така че не е необходимо да освобождавате изрично паметта, създадена за който и да е обект. Всъщност не можете. Няма еквивалент на C ++ delete оператор или free() функция в C. Но това не означава, че можете просто да забравите за всички обекти, след като приключите с използването им. Много видове обекти капсулират някакъв друг тип системен ресурс (напр. Дисков файл, връзка с база данни, мрежов сокет и т.н.). Оставянето на тези ресурси отворени може бързо да изчерпи общия брой системни ресурси, да влоши производителността и в крайна сметка да доведе до програмни грешки.

Докато методът на деструктор може да бъде дефиниран за всеки клас C #, проблемът с деструкторите (наричани също финализатори в C #) е, че не можете да знаете със сигурност кога ще бъдат извикани. Те се извикват от събирача на боклука (на отделна нишка, което може да причини допълнителни усложнения) в неопределено време в бъдеще. Опитвайки се да заобиколите тези ограничения, като принудите събирането на боклука с GC.Collect() не е C # най-добра практика , тъй като това ще блокира нишката за неизвестен период от време, докато събира всички обекти, допустими за събиране.

Това не означава, че няма добри приложения за финализаторите, но освобождаването на ресурси по детерминиран начин не е едно от тях. По-скоро, когато работите с връзка с файл, мрежа или база данни, искате изрично да освободите основния ресурс веднага щом приключите с него.

Изтичането на ресурси е проблем в почти всяка среда . C # обаче предоставя механизъм, който е здрав и лесен за използване, който, ако се използва, може да направи течовете много по-рядко явление. .NET рамката определя IDisposable интерфейс, който се състои само от Dispose() метод. Всеки обект, който реализира IDisposable очаква този метод да бъде извикан винаги, когато потребителят на обекта приключи с манипулирането му. Това води до изрично, детерминирано освобождаване на ресурси.

Ако създавате и изхвърляте обект в контекста на единичен кодов блок, по принцип е непростимо да забравите да извикате Dispose(), защото C # предоставя using изявление, което ще гарантира Dispose() се извиква, без значение как е излязъл блока на кода (независимо дали е изключение, оператор за връщане или просто затваряне на блока). И да, това е същото using изявление, споменато по-рано, което се използва за включване на C # пространства от имена в горната част на вашия файл. Той има втора, напълно несвързана цел, за която много разработчици на C # не знаят; а именно да се гарантира, че Dispose() се извиква на обект при излизане от блока на кода:

using (FileStream myFile = File.OpenRead('foo.txt')) { myFile.Read(buffer, 0, 100); }

Чрез създаването на using блок в горния пример, вие със сигурност знаете, че myFile.Dispose() ще бъде извикан веднага щом приключите с файла, независимо дали Read() хвърля изключение.

Често срещана грешка в програмирането на C # # 9: Избягване на изключенията

C # продължава да налага изпълнението на безопасността на типа по време на изпълнение. Това ви позволява да определите много видове грешки в C # много по-бързо, отколкото в езици като C ++, където преобразуванията на дефектни типове могат да доведат до произволни стойности, присвоени на полетата на обекта. За пореден път програмистите могат да пропилеят тази страхотна функция, което води до проблеми с C #. Те попадат в този капан, защото C # предоставя два различни начина за извършване на нещата, един, който може да създаде изключение, и един, който няма. Някои ще се отклонят от маршрута на изключението, като смятат, че липсата на опит за записване / улов на блок им спестява малко кодиране.

Например, тук са два различни начина за извършване на явен тип гласове в C #:

// METHOD 1: // Throws an exception if account can't be cast to SavingsAccount SavingsAccount savingsAccount = (SavingsAccount)account; // METHOD 2: // Does NOT throw an exception if account can't be cast to // SavingsAccount; will just set savingsAccount to null instead SavingsAccount savingsAccount = account as SavingsAccount;

Най-очевидната грешка, която би могла да възникне при използването на метод 2, би била невъзможността да се провери връщаната стойност. Това вероятно би довело до евентуално NullReferenceException, което може да се появи на много по-късен етап, което ще направи много по-трудно проследяването на източника на проблема. За разлика от това метод 1 веднага би изхвърлил InvalidCastException правейки източника на проблема много по-непосредствено очевиден.

Освен това, дори ако не забравяте да проверите връщаната стойност в Метод 2, какво ще правите, ако установите, че е нула? Методът, който пишете, подходящо ли е място за докладване на грешка? Има ли нещо друго, което можете да опитате, ако този актьор се провали? В противен случай хвърлянето на изключение е правилното нещо, което можете да направите, така че може да позволите това да се случи възможно най-близо до източника на проблема.

Ето няколко примера за други често срещани двойки методи, при които единият хвърля изключение, а другият не:

int.Parse(); // throws exception if argument can’t be parsed int.TryParse(); // returns a bool to denote whether parse succeeded IEnumerable.First(); // throws exception if sequence is empty IEnumerable.FirstOrDefault(); // returns null/default value if sequence is empty

Някои разработчици на C # са толкова „неблагоприятни“, че автоматично приемат, че методът, който не създава изключение, е по-добър. Въпреки че има някои избрани случаи, когато това може да е вярно, това изобщо не е правилно като обобщение.

Като конкретен пример, в случай, че имате алтернативно легитимно (например по подразбиране) действие, което трябва да се предприеме, ако е генерирано изключение, тогава подходът без изключение може да бъде легитимен избор. В такъв случай може наистина да е по-добре да напишете нещо подобно:

if (int.TryParse(myString, out myInt)) { // use myInt } else { // use default value }

вместо:

try { myInt = int.Parse(myString); // use myInt } catch (FormatException) { // use default value }

Неправилно е обаче да се приема, че TryParse следователно е задължително 'по-добрият' метод. Понякога това е така, понякога не е така. Ето защо има два начина да го направите. Използвайте правилния за контекста, в който се намирате, като не забравяте, че изключенията със сигурност могат да бъдат вашият приятел като разработчик.

Често срещана грешка в програмирането на C # # 10: Разрешаване на натрупването на предупреждения на компилатора

Въпреки че този проблем определено не е специфичен за C #, той е особено възхитителен при програмирането на C #, тъй като изоставя предимствата на стриктната проверка на типа, предлагана от компилатора на C #.

Предупрежденията се генерират по причина. Докато всички грешки на компилатора на C # означават дефект във вашия код, много предупреждения също го правят. Това, което различава двете, е, че в случай на предупреждение компилаторът няма проблем да издава инструкциите, които вашият код представлява. Въпреки това той намира вашия код за малко рибен и има разумна вероятност кодът ви да не отразява точно вашите намерения.

Често срещан прост пример в името на този урок за програмиране на C # е, когато модифицирате алгоритъма си, за да елиминирате използването на променлива, която сте използвали, но забравяте да премахнете декларацията на променливата. Програмата ще работи перфектно, но компилаторът ще маркира безполезната декларация на променлива. Фактът, че програмата работи перфектно, кара програмистите да пренебрегнат, за да отстранят причината за предупреждението. Освен това кодерите се възползват от функцията на Visual Studio, която им улеснява да скрият предупрежденията в прозореца „Списък с грешки“, за да могат да се фокусират само върху грешките. Не отнема много време, докато има десетки предупреждения, всички те блажено игнорирани (или още по-лошо, скрити).

Но ако игнорирате този тип предупреждение, рано или късно, нещо подобно може много да намери пътя си във вашия код:

class Account { int myId; int Id; // compiler warned you about this, but you didn’t listen! // Constructor Account(int id) { this.myId = Id; // OOPS! } }

И със скоростта, която Intellisense ни позволява да пишем код, тази грешка не е толкова невероятна, колкото изглежда.

Вече имате сериозна грешка във вашата програма (въпреки че компилаторът я е отбелязал само като предупреждение поради вече обяснените причини) и в зависимост от това колко сложна е вашата програма, можете да загубите много време, за да я проследите. Ако на първо място обърнахте внимание на това предупреждение, щяхте да избегнете този проблем с обикновена корекция от пет секунди.

Не забравяйте, че компилаторът C Sharp ви дава много полезна информация за надеждността на вашия код ... ако слушате. Не пренебрегвайте предупрежденията. Те обикновено отнемат само няколко секунди, за да се оправят, а поправянето на нови, когато се случат, може да ви спести часове. Обучете се да очаквате прозореца на Visual Studio „Списък с грешки“ да показва „0 грешки, 0 предупреждения“, така че всички предупреждения да ви карат да се чувствате неудобно, за да ги адресирате незабавно.

Разбира се, има изключения от всяко правило. Съответно, може да има моменти, когато вашият код ще изглежда малко съобразителен за компилатора, въпреки че е точно такъв, какъвто сте го замислили. В тези много редки случаи използвайте #pragma warning disable [warning id] около само кода, който задейства предупреждението, и само за идентификатора на предупреждението, който задейства. Това ще потисне това предупреждение и само това предупреждение, за да можете да останете нащрек за нови.

Обобщение

C # е мощен и гъвкав език с много механизми и парадигми, които могат значително да подобрят производителността. Както при всеки софтуерен инструмент или език, обаче, ограниченото разбиране или оценка на неговите възможности понякога може да бъде по-скоро пречка, отколкото полза, оставяйки човек в пословичното състояние на „да знае достатъчно, за да бъде опасен“.

Използване на урок C Sharp като този запознайте се с ключовите нюанси на C #, като (но в никакъв случай не само) проблемите, повдигнати в тази статия, ще помогнат за оптимизацията на C #, като същевременно се избегнат някои от най-често срещаните му клопки на езика.

Свързани: 6 основни въпроса за интервю за C #

Разбиране на основите

Какво е C #?

C # е един от няколкото програмни езика, насочени към Microsoft CLR, който автоматично му дава предимствата на междуезичната интеграция и обработка на изключения, подобрена сигурност, опростен модел за взаимодействие с компоненти и услуги за отстраняване на грешки и профилиране.

Каква е разликата между C ++ и C #?

C ++ и C # са два напълно различни езика, въпреки приликите в името и синтаксиса. C # е проектиран да бъде донякъде по-високо ниво от C ++ и двата езика също възприемат различни подходи към детайли като кой определя дали даден параметър се предава по препратка или по стойност.

Защо се използва C #?

C # се използва по много причини, но предимствата на набора от инструменти на Microsoft CLR и голямата общност на разработчици са две основни насоки към езика.

Съвети и съображения при избора на шрифт (с инфографика)

Ui Design

Съвети и съображения при избора на шрифт (с инфографика)
Пълен преглед на най-добрите инструменти за визуализация на данни

Пълен преглед на най-добрите инструменти за визуализация на данни

Ui Design

Популярни Публикации
Инсталиране на Django на IIS: Урок стъпка по стъпка
Инсталиране на Django на IIS: Урок стъпка по стъпка
Въведение в търговията с дълбоко обучение в хедж фондове
Въведение в търговията с дълбоко обучение в хедж фондове
Ръководство за здрави модулни и интеграционни тестове с JUnit
Ръководство за здрави модулни и интеграционни тестове с JUnit
Разработване на мобилни уеб приложения: кога, защо и как
Разработване на мобилни уеб приложения: кога, защо и как
Как да накараме отдалечената работа да работи за вас
Как да накараме отдалечената работа да работи за вас
 
Месец в живота - Временни роли на финансовия директор и най-добри практики
Месец в живота - Временни роли на финансовия директор и най-добри практики
Android DDMS: Ръководство за Ultimate Android Console
Android DDMS: Ръководство за Ultimate Android Console
Щъркел, част 2: Създаване на анализатор на изрази
Щъркел, част 2: Създаване на анализатор на изрази
Плащане напред: Разбиране на изкупувания с ливъридж
Плащане напред: Разбиране на изкупувания с ливъридж
Убеждаване и преместване - Ръководство за принципите на дизайна на движението
Убеждаване и преместване - Ръководство за принципите на дизайна на движението
Категории
ИновацияДругиПродукти Хора И ЕкипиAgile TalentUi DesignНачин На ЖивотИнженерно УправлениеПланиране И ПрогнозиранеПъргавПубликуване

© 2023 | Всички Права Запазени

socialgekon.com