Скрытые возможности C#?
Это пришло мне в голову после того, как я узнал следующее из этого вопроса:
where T : struct
Мы, разработчики C#, все знаем основы C#. Я имею в виду объявления, условия, циклы, операторы и т. Д.
Некоторые из нас даже освоили такие вещи, как Generics, анонимные типы, лямбды, LINQ,...
Но каковы наиболее скрытые функции или уловки C#, которые даже фанаты C#, наркоманы, эксперты едва знают?
Вот некоторые выявленные особенности:
Ключевые слова
yield
Майкл Стумvar
Майкл Стумusing()
заявление кокосаreadonly
Кокосas
Майк Стоунas
/is
Эд Свангренas
/is
(улучшено) от Rocketpantsdefault
Deathofratsglobal::
Pzycomanusing()
блоки от AlexCusevolatile
Якуб Штурцextern alias
Якуб Штурц
Атрибуты
DefaultValueAttribute
Майкл СтумObsoleteAttribute
от DannySmurfDebuggerDisplayAttribute
СтюDebuggerBrowsable
а такжеDebuggerStepThrough
по bdukesThreadStaticAttribute
МарксидадFlagsAttribute
Мартин КларкConditionalAttribute
от AndrewBurns
Синтаксис
??
(объединить нули) оператор по Kokos- Числовая маркировка Ника Берарди
where T:new
Ларс Мухлум- Неявные дженерики от Кейта
- Однопараметрические лямбды Китом
- Авто свойства Кит
- Псевдонимы пространства имен Китом
- Дословные строковые литералы с @ Патриком
enum
значения по lfoust- @variablenames от marxidad
event
операторы Марксидад- Строковые скобки формата от Portman
- Модификаторы доступа к свойствам от xanadont
- Условный (троичный) оператор (
?:
) Джейсон checked
а такжеunchecked
операторы Биной Антонийimplicit and explicit
операторы Флори
Особенности языка
- Обнуляемые типы от Брэда Баркера
- Анонимные типы Китом
__makeref __reftype __refvalue
Иуда Химанго- Инициализаторы объектов от lomaxx
- Формат строки Дэвида в Дакоте
- Методы расширения от marxidad
partial
методы Джон Эриксон- Директивы препроцессора Джона Асбека
DEBUG
Директива препроцессора Роберта Дургина- Перегрузка оператора по SefBkn
- Вывод типа по чакриту
- Булевы операторы выведены на следующий уровень Робом Гофом
- Передать переменную типа значения в качестве интерфейса без бокса от Романа Бойко
- Программно определить объявленный тип переменной Романом Бойко
- Статические Конструкторы Крисом
- Упрощенное визуальное / сжатое ORM-картирование с использованием LINQ от roosteronacid
__arglist
Зак Боулинг
Особенности Visual Studio
- Выделить блок текста в редакторе Химадри
- Фрагменты от DannySmurf
Фреймворк
TransactionScope
от KiwiBastardDependantTransaction
от KiwiBastardNullable<T>
от IainMHMutex
по DiagoSystem.IO.Path
по возрастуWeakReference
Хуан Мануэль
Методы и свойства
String.IsNullOrEmpty()
метод от KiwiBastardList.ForEach()
метод от KiwiBastardBeginInvoke()
,EndInvoke()
методы Уилла ДинаNullable<T>.HasValue
а такжеNullable<T>.Value
свойства RismoGetValueOrDefault
метод Джон Шиэн
Советы и хитрости
- Хороший метод для обработчиков событий от Andreas HR Nilsson
- Прописные сравнения Джона
- Доступ к анонимным типам без отражения от dp
- Быстрый способ ленивой реализации свойств коллекции с помощью Will
- JavaScript-анонимные inline-функции от roosteronacid
Другой
- сетевые модули от kokos
- LINQBridge от Дункан Смарт
- Параллельные расширения от Джоэла Коухорна
296 ответов
Это не C# само по себе, но я не видел никого, кто действительно использует System.IO.Path.Combine()
в той степени, в которой они должны. На самом деле, весь класс Path действительно полезен, но никто не использует его!
Я готов поспорить, что каждое производственное приложение имеет следующий код, хотя и не должно:
string path = dir + "\\" + fileName;
лямбды и типовые выводы недооценены. Lambdas может иметь несколько операторов, и они автоматически дублируются как совместимый объект делегата (просто убедитесь, что подпись совпадает), как в:
Console.CancelKeyPress +=
(sender, e) => {
Console.WriteLine("CTRL+C detected!\n");
e.Cancel = true;
};
Обратите внимание, что у меня нет new CancellationEventHandler
и при этом я не должен указывать типы sender
а также e
они могут быть выведены из события. Вот почему это менее громоздко, чтобы писать весь delegate (blah blah)
что также требует, чтобы вы указали типы параметров.
Лямбды не нужно ничего возвращать, и вывод типа очень мощный в таком контексте.
И кстати, вы всегда можете вернуть Lambdas, которые делают Lambdas в смысле функционального программирования. Например, вот лямбда, которая делает лямбду, которая обрабатывает событие Button.Click:
Func<int, int, EventHandler> makeHandler =
(dx, dy) => (sender, e) => {
var btn = (Button) sender;
btn.Top += dy;
btn.Left += dx;
};
btnUp.Click += makeHandler(0, -1);
btnDown.Click += makeHandler(0, 1);
btnLeft.Click += makeHandler(-1, 0);
btnRight.Click += makeHandler(1, 0);
Обратите внимание на цепочку: (dx, dy) => (sender, e) =>
Вот почему я счастлив, что взял класс функционального программирования:-)
Кроме указателей в C, я думаю, что это еще одна фундаментальная вещь, которую вы должны изучить:-)
От Рика Строля:
Вы можете связать?? оператор, так что вы можете сделать кучу нулевых сравнений.
string result = value1 ?? value2 ?? value3 ?? String.Empty;
Псевдонимы:
using ASimpleName = Dictionary<string, Dictionary<string, List<string>>>;
Это позволяет использовать ASimpleName
, вместо Dictionary<string, Dictionary<string, List<string>>>
,
Используйте его, когда вы будете использовать одну и ту же универсальную большую длинную сложную вещь во многих местах.
Из CLR через C#:
При нормализации строк настоятельно рекомендуется использовать ToUpperInvariant вместо ToLowerInvariant, потому что Microsoft оптимизировала код для сравнения в верхнем регистре.
Я помню, как однажды мой коллега всегда сравнивал строки в верхний регистр перед сравнением. Я всегда удивлялся, почему он это делает, потому что я чувствую, что более "естественно" сначала перевести в нижний регистр. После прочтения книги теперь я знаю почему.
Мой любимый трюк - использование оператора null coalesce и скобок для автоматического создания экземпляров коллекций для меня.
private IList<Foo> _foo;
public IList<Foo> ListOfFoo
{ get { return _foo ?? (_foo = new List<Foo>()); } }
Избегайте проверки на нулевые обработчики событий
Добавление пустого делегата к событиям при объявлении, устранение необходимости всегда проверять событие на нулевое значение, прежде чем вызывать его, это здорово. Пример:
public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click = delegate {}; // add empty delegate!
Позволь тебе сделать это
public void DoSomething()
{
Click(this, "foo");
}
Вместо этого
public void DoSomething()
{
// Unnecessary!
MyClickHandler click = Click;
if (click != null) // Unnecessary!
{
click(this, "foo");
}
}
Пожалуйста, также посмотрите эту связанную дискуссию и этот блог Эрика Липперта на эту тему (и возможные недостатки).
Все остальное, плюс
1) неявные дженерики (почему только для методов, а не для классов?)
void GenericMethod<T>( T input ) { ... }
//Infer type, so
GenericMethod<int>(23); //You don't need the <>.
GenericMethod(23); //Is enough.
2) простые лямбды с одним параметром:
x => x.ToString() //simplify so many calls
3) анонимные типы и инициализаторы:
//Duck-typed: works with any .Add method.
var colours = new Dictionary<string, string> {
{ "red", "#ff0000" },
{ "green", "#00ff00" },
{ "blue", "#0000ff" }
};
int[] arrayOfInt = { 1, 2, 3, 4, 5 };
Другой:
4) Авто свойства могут иметь разные области видимости:
public int MyId { get; private set; }
Спасибо @pzycoman за напоминание:
5) Псевдонимы пространства имен (не то, что вам, вероятно, понадобится это конкретное различие):
using web = System.Web.UI.WebControls;
using win = System.Windows.Forms;
web::Control aWebControl = new web::Control();
win::Control aFormControl = new win::Control();
Я давно не знал ключевое слово "как".
MyClass myObject = (MyClass) obj;
против
MyClass myObject = obj as MyClass;
Второй вернет ноль, если obj не является MyClass, а не сгенерирует исключение приведения класса.
Две вещи, которые мне нравятся, это свойства Automatic, так что вы можете свернуть свой код еще дальше:
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
становится
public string Name { get; set;}
Также инициализаторы объекта:
Employee emp = new Employee();
emp.Name = "John Smith";
emp.StartDate = DateTime.Now();
становится
Employee emp = new Employee {Name="John Smith", StartDate=DateTime.Now()}
Ключевое слово "default" в универсальных типах:
T t = default(T);
приводит к нулю, если T является ссылочным типом, и 0, если это int, и false, если это логическое значение, и так далее.
Атрибуты в целом, но больше всего DebuggerDisplay. Спасает тебя годы.
Символ @ указывает компилятору игнорировать любые escape-символы в строке.
Просто хотел уточнить это... он не говорит ему игнорировать escape-символы, он на самом деле говорит компилятору интерпретировать строку как литерал.
Если у вас есть
string s = @"cat
dog
fish"
он будет фактически распечатан как (обратите внимание, что он даже включает пробелы, используемые для отступа):
cat
dog
fish
Я думаю, что одна из наиболее недооцененных и менее известных функций C# (.NET 3.5) - деревья выражений, особенно в сочетании с Generics и Lambdas. Это подход к созданию API, который используют более новые библиотеки, такие как NInject и Moq.
Например, предположим, что я хочу зарегистрировать метод с помощью API, и что API должен получить имя метода
Учитывая этот класс:
public class MyClass
{
public void SomeMethod() { /* Do Something */ }
}
Раньше разработчики часто делали это со строками и типами (или что-то еще, в основном, на основе строк):
RegisterMethod(typeof(MyClass), "SomeMethod");
Ну, это отстой из-за отсутствия строгой типизации. Что если я переименую "SomeMethod"? Теперь, в 3.5, я могу сделать это строго типизированным способом:
RegisterMethod<MyClass>(cl => cl.SomeMethod());
В котором класс RegisterMethod использует Expression<Action<T>>
как это:
void RegisterMethod<T>(Expression<Action<T>> action) where T : class
{
var expression = (action.Body as MethodCallExpression);
if (expression != null)
{
// TODO: Register method
Console.WriteLine(expression.Method.Name);
}
}
Это одна из причин, по которой я влюблен в Lambdas и Expression Trees прямо сейчас.
" доходность " пришла бы мне в голову. Некоторые из атрибутов, таких как [DefaultValue()], также среди моих любимых.
Ключевое слово " var " немного более известно, но то, что вы можете использовать его и в приложениях.NET 2.0 (если вы используете компилятор.NET 3.5 и устанавливаете его для вывода кода 2.0), кажется не очень известным Что ж.
Редактировать: kokos, спасибо за указание на?? оператор, это действительно очень полезно. Так как в нем сложно найти Google (так как?? просто игнорируется), вот страница документации MSDN для этого оператора: ?? Оператор (C# Reference)
Я склонен находить, что большинство разработчиков на C# не знают о типах, которые можно обнулять. В основном, примитивы, которые могут иметь нулевое значение.
double? num1 = null;
double num2 = num1 ?? -100;
Установите для nullable double num1 значение null, затем для обычного double num2 установите значение num1 или -100, если num1 было равно нулю.
http://msdn.microsoft.com/en-us/library/1t3y8s4s(VS.80).aspx
Еще одна вещь о типе Nullable:
DateTime? tmp = new DateTime();
tmp = null;
return tmp.ToString();
это возврат String.Empty. Проверьте эту ссылку для более подробной информации
Вот некоторые интересные скрытые возможности C# в форме недокументированных ключевых слов C#:
__makeref
__reftype
__refvalue
__arglist
Это недокументированные ключевые слова C# (даже Visual Studio распознает их!), Которые были добавлены для более эффективной упаковки / распаковки до использования обобщений. Они работают в координации со структурой System.TypedReference.
Также есть __arglist, который используется для списков параметров переменной длины.
Одна вещь, о которой мало кто знает, это System.WeakReference - очень полезный класс, который отслеживает объект, но все же позволяет сборщику мусора собирать его.
Наиболее полезной "скрытой" функцией будет ключевое слово yield return. Это на самом деле не скрыто, но многие люди не знают об этом. LINQ построен на этом; это позволяет выполнять запросы с задержкой, генерируя конечный автомат. Раймонд Чен недавно опубликовал информацию о внутренних мелочах.
Союзы (вид разделяемой памяти C++) в чистом, безопасном C#
Не прибегая к небезопасному режиму и указателям, вы можете сделать так, чтобы члены класса совместно использовали пространство памяти в классе / структуре. Учитывая следующий класс:
[StructLayout(LayoutKind.Explicit)]
public class A
{
[FieldOffset(0)]
public byte One;
[FieldOffset(1)]
public byte Two;
[FieldOffset(2)]
public byte Three;
[FieldOffset(3)]
public byte Four;
[FieldOffset(0)]
public int Int32;
}
Вы можете изменить значения полей байтов, манипулируя полем Int32 и наоборот. Например, эта программа:
static void Main(string[] args)
{
A a = new A { Int32 = int.MaxValue };
Console.WriteLine(a.Int32);
Console.WriteLine("{0:X} {1:X} {2:X} {3:X}", a.One, a.Two, a.Three, a.Four);
a.Four = 0;
a.Three = 0;
Console.WriteLine(a.Int32);
}
Выводы это:
2147483647
FF FF FF 7F
65535
просто добавьте с помощью System.Runtime.InteropServices;
Использование @ для имен переменных, которые являются ключевыми словами.
var @object = new object();
var @string = "";
var @if = IpsoFacto();
Если вы хотите выйти из вашей программы без вызова каких-либо блоков finally или финализаторов, используйте FailFast:
Environment.FailFast()
Возврат анонимных типов из метода и доступ к членам без отражения.
// Useful? probably not.
private void foo()
{
var user = AnonCast(GetUserTuple(), new { Name = default(string), Badges = default(int) });
Console.WriteLine("Name: {0} Badges: {1}", user.Name, user.Badges);
}
object GetUserTuple()
{
return new { Name = "dp", Badges = 5 };
}
// Using the magic of Type Inference...
static T AnonCast<T>(object obj, T t)
{
return (T) obj;
}
Вот полезный пример для регулярных выражений и путей к файлам:
"c:\\program files\\oldway"
@"c:\program file\newway"
Символ @ указывает компилятору игнорировать любые escape-символы в строке.
Примеси. По сути, если вы хотите добавить функцию к нескольким классам, но не можете использовать один базовый класс для всех из них, попросите каждый класс реализовать интерфейс (без членов). Затем напишите метод расширения для интерфейса, т.е.
public static DeepCopy(this IPrototype p) { ... }
Конечно, некоторая ясность принесена в жертву. Но это работает!
Не уверен, почему кто-нибудь захочет использовать Nullable
.:-)
Верно, Неверно, FileNotFound?
Этот не "спрятан" так, как его неправильно назвали.
Большое внимание уделяется алгоритмам "карта", "уменьшить" и "фильтр". Большинство людей не осознают, что в.NET 3.5 были добавлены все три из этих алгоритмов, но он дал им очень SQL-имена, основываясь на том факте, что они являются частью LINQ.
"карта" => Выбрать
Преобразует данные из одной формы в другую"уменьшить" => Агрегировать
Объединяет значения в один результат"фильтр" => где
Фильтрует данные по критерию
Возможность использовать LINQ для выполнения встроенной работы с коллекциями, которые использовались для итераций и условных выражений, может быть невероятно полезной. Стоит узнать, как все методы расширения LINQ могут сделать ваш код более компактным и обслуживаемым.
Если вы пытаетесь использовать фигурные скобки внутри выражения String.Format...
int foo = 3;
string bar = "blind mice";
String.Format("{{I am in brackets!}} {0} {1}", foo, bar);
//Outputs "{I am in brackets!} 3 blind mice"
- ?? - оператор коалесцирования
- using ( оператор / директива) - отличное ключевое слово, которое можно использовать не только для вызова Dispose
- только для чтения - следует использовать больше
- сетевые модули - очень плохо, в Visual Studio нет поддержки
@ Эд, я немного неохотно пишу об этом, так как это немного больше, чем придирки. Однако я хотел бы отметить, что в вашем примере кода:
MyClass c;
if (obj is MyClass)
c = obj as MyClass
Если вы собираетесь использовать "is", зачем использовать безопасное приведение с использованием "as"? Если вы убедились, что obj действительно MyClass, это стандартное приведение:
c = (MyClass)obj
... никогда не потерпит неудачу.
Точно так же вы могли бы просто сказать:
MyClass c = obj as MyClass;
if(c != null)
{
...
}
Я не знаю достаточно о внутренностях.NET, чтобы быть уверенным, но мои инстинкты говорят мне, что это сократит не более двух операций приведения типов до максимум одной. Вряд ли в любом случае сломать процессинговый банк; лично я думаю, что последняя форма выглядит чище.
Возможно, не продвинутая техника, но та, которую я вижу все время, которая сводит меня с ума:
if (x == 1)
{
x = 2;
}
else
{
x = 3;
}
может быть сжат до:
x = (x==1) ? 2 : 3;