Pull to refresh

C# .NET: Пять маленьких чудес, которые сделают ваш код лучше. Часть 1

Reading time5 min
Views26K
Здравствуй, Хабрасообщество. Хотелось бы представить на суд твой свои переводы серии статей Джеймса Майкла Харе (James Michael Hare) «Маленькие чудеса C#». Итак, первая часть перед вами!
Эти маленькие советы и рекомендации сделают ваш код более кратким, производительным и обслуживаемым. Наверное, многие из вас знают обо всех или некоторых из них, но игнорируют их, либо просто не знают.

1.Оператор ??


Этот маленький оператор может быть чрезвычайно полезным в некоторых случаях. Как часто случается такое, что у вас есть переменная, значение которой вы бы хотели использовать, но если это значение-null, то вы хотите заменить его значением по умолчанию? Допустим, вы хотите присвоить строку переменной, но если строка равна null, вы хотите подставить пустую строку вместо нее:
Можно, конечно, написать условное выражение, используя if:
string name = value; if (value == null) { name = string.Empty; }

Или еще лучше, если использовать тернарный оператор «?»:
string name = (value != null) ? value : string.Empty;

О, уже более кратко, но мы можем улучшить и этот код! В C# имеется оператор ??, представляющий собой укороченную версию оператора? с проверкой значения на null:
string name = value ?? string.Empty;

Очень лаконично и красиво! Вы можете написать вспомогательный метод, чтобы урезать строку и возвращать null, если строка состоит только из пробелов, так что он сможет использовать ??..
public static class StringUtility 
{ 
public static string TrimToNull(string source) 
{ 
return string.IsNullOrWhiteSpace(source) ? null : source.Trim(); 
} 
}

Также этот оператор может быть использован, чтобы преобразовать пустые строки в значение по умолчанию:
string name = StringUtility.TrimToNull(value) ?? "Не определено";

2.Приведение типов с помощью as


Сколько раз вы видели подобный код:
if (employee is SalariedEmployee) 
{ 
var salEmp = (SalariedEmployee)employee; 
pay = salEmp.WeeklySalary; 
// ... 
}

Он является излишним, так как вы выполняете проверку типа дважды. Первый раз-проверка в условном операторе, и второй-приведение типов. Каждый раз, когда вы обнаруживаете, что идете по этому пути, предпочтите ему приведение типов с помощью as. Это удобный метод приведения, поскольку оно возвращает ссылку на тип, если типы совместимы, и null, если нет:
var salEmployee = employee as SalariedEmployee; 
if (salEmployee != null) 
{ 
pay = salEmployee.WeeklySalary;
 // ... 
}

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

3.Автоматические свойства


Все мы знаем о свойствах в C#, и большинство знает об автоматических свойствах. Они превращают следующий код, содержащий два обычных свойства и два приватных поля:
public class Point 
{ 
int _x, _y; 
public int X 
{ 
{ return _x; } 
set { _x = value; } 
} 
public int Y 
{ 
get { return _y; } 
set { _y = value; } 
} 
}

в гораздо более краткий и понятный:
public class Point 
{ 
public int X { get; set; } 
public int Y { get; set; } 
}

Намного короче! Всякий раз, когда у вас имеется свойство, просто устанавливающее или возвращающее значение приватного поля, вам следует предпочти автоматические свойства обыкновенным, хотя бы для того, чтобы не писать много кода. Все что вам нужно, это написать свойство с аксессорами, не содержащими кода, и компилятор автоматически сгенерирует поля для вас «за кулисами».
Вы можете также сделать свойство с ассиметричными уровнями доступа! То есть вы может сделать автоматическое свойство доступным только для чтения или только для записи:
public class Asymetrical 
{ 
public string ThisIsReadOnly { get; private set; } 
public double ThisIsWriteOnly { private get; set; } 
}

Это облегчает задачу создания свойств, которые могут быть установлены только самим классом, но могут быть прочитаны публично, и наоборот.

4. Класс Stopwatch (секундомер)


Сколько раз вы хотели засечь, сколько времени выполняется кусок кода? Может быть, вы пытались отследить среднее время выполнения запроса или другую подобную информацию, или, возможно, пытались отправить предупреждающее сообщение, когда метод требует более одной секунды для выполнения?
Вы могли бы сделать это, используя DateTime:
DateTime start = DateTime.Now; SomeCodeToTime(); 
DateTime end = DateTime.Now; 
Console.WriteLine("Выполнение метода заняло {0} мс", (end-start).TotalMilliseconds);

Но DateTime неточен для такого рода вычислений. Вы можете обратиться к таймеру более высокой точности через Win32 API, но этот способ более «грязный» и подвержен ошибкам.
Так что же делать разработчику? Использовать класс Stopwatch, который расположен в пространстве имен System.Diagnostics. Класс Stopwatch упрощает использование высокоточного таймера:
var timer = Stopwatch.StartNew(); 
SomeCodeToTime(); 
timer.Stop(); 
Console.WriteLine("Выполнение метода заняло {0} мс", timer.ElapsedMilliseconds);

Гораздо более точно. Обратите внимание, что есть статический метод, который создает и запускает секундомер. Также есть возможность создать секундомер с использование new и запустить, когда понадобится.

5. Методы TimeSpan


Сколько раз вы видели код, подобный нижеследующему, и интересовались, как долго должен длиться «сон»?
Thread.Sleep(50);

Пятьдесят секунд? Миллисекунд? На самом деле это миллисекунды. Но что делать, когда наткнешься на что-то подобное:
void PerformRemoteWork(int timeout) { ... }

Что это за тайм-аут? Секунды? Минуты? Миллисекунды? Все завист от разработчиков! Я видел тайм-ауты и интервалы в BCL и пользовательском коде, записанные и как секунды, и как миллисекунды. Для этого почти всегда лучше использовать TimeSpan, поскольку он делает это гораздо менее двусмысленным способом:
void PerformRemoteWork(TimeSpan timeout) { ... }

Теперь не нужно беспокоиться об единицах, потому что они были указаны при создании объекта TimeSpan:
PerformRemoteWork(new TimeSpan(0, 0, 0, 0, 50));

Устраняется неоднозначность с точки зрения самого метода, но не вызывающего кода. Для вызывающего кода пройдут 50 секунд? Или миллисекунд? Кажется, у нас есть подобная проблема! Некоторую путаницу может внести и то, что в классе TimeSpan есть пять конструкторов и все они неоднозначны:
TimeSpan(); 
TimeSpan(long ticks); 
TimeSpan(int hours, int minutes, int seconds); 
TimeSpan(int days, int hours, int minutes, int seconds); 
TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds);

Используйте статические методы класса TimeSpan. Это позволит избежать неоднозанчности при создании объекта TimeSpan:
PerformRemoteWork(TimeSpan.FromMilliseconds(50));

Теперь двусмысленности нет, и все прекрасно читается! Нет шансов неправильно истолковать параметры. По аналогии используйте следующие методы:
TimeSpan.FromDays(double days); 
TimeSpan.FromHours(double hours);
TimeSpan.FromMinutes(double minutes); 
TimeSpan.FromSeconds(double seconds);


Так что вы можете указывать интервал однозначным образом. Это работает и со статическими полями, предназначенными только для чтения:
public sealed class MessageProducer 
{ 
    private static readonly _defaultTimeout = TimeSpan.FromSeconds(30); 
}


Резюме


Надеюсь, вам понравились эти пять маленьких чудес, и у меня есть что предложить вам на следующей неделе. Я надеюсь, вы нашли хоть одно, о котором вы не знали или забыли, и теперь можете использовать!
Tags:
Hubs:
Total votes 87: ↑59 and ↓28+31
Comments35

Articles