Скрытые возможности C #? [закрыто]


1475

Это пришло мне в голову после того, как я узнал следующее из этого вопроса :

where T : struct

Мы, разработчики C #, все знаем основы C #. Я имею в виду объявления, условия, циклы, операторы и т. Д.

Некоторые из нас даже освоили такие вещи, как Generics , анонимные типы , лямбды , LINQ , ...

Но каковы наиболее скрытые функции или уловки C #, которые даже фанаты C #, наркоманы, эксперты едва знают?

Вот некоторые выявленные особенности:


Ключевые слова

Атрибуты

Синтаксис

Особенности языка

Особенности Visual Studio

Фреймворк

Методы и свойства

Советы и хитрости

  • Хороший метод для обработчиков событий от Andreas HR Nilsson
  • Прописные сравнения Джона
  • Доступ к анонимным типам без отражения от dp
  • Быстрый способ ленивой реализации свойств коллекции с помощью Will
  • Подобные JavaScript анонимные inline-функции от roosteronacid

Другой

Ответы:


752

Это не C # само по себе, но я не видел никого, кто действительно использует System.IO.Path.Combine()в той степени, в которой они должны. На самом деле, весь класс Path действительно полезен, но никто не использует его!

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

string path = dir + "\\" + fileName;

584

лямбды и вывод типов недооценены. 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, я думаю, что это еще одна фундаментальная вещь, которую вы должны изучить :-)


528

От Рика Строля :

Вы можете связать оператор, так что вы можете сделать кучу нулевых сравнений.

string result = value1 ?? value2 ?? value3 ?? String.Empty;

454

Псевдонимы:

using ASimpleName = Dictionary<string, Dictionary<string, List<string>>>;

Это позволяет вам использовать ASimpleNameвместоDictionary<string, Dictionary<string, List<string>>> .

Используйте его, когда вы будете использовать одну и ту же общую большую длинную сложную вещь во многих местах.


438

Из CLR через C # :

При нормализации строк настоятельно рекомендуется использовать ToUpperInvariant вместо ToLowerInvariant, потому что Microsoft оптимизировала код для сравнения в верхнем регистре .

Я помню, как однажды мой коллега всегда сравнивал строки в верхний регистр перед сравнением. Я всегда удивлялся, почему он это делает, потому что я чувствую, что более «естественно» сначала перевести в нижний регистр. После прочтения книги теперь я знаю почему.


254
Когда вы «конвертируете строку в верхний регистр», вы создаете второй временный строковый объект. Я думал, что этот вид сравнения не является предпочтительным, что лучший способ был: String.Equals (stringA, stringB, StringComparison.CurrentCultureIgnoreCase), который вообще не создает эту одноразовую строку.
Энтони

32
Какую оптимизацию вы можете выполнить при сравнении строк в верхнем регистре, которые нельзя выполнить в строках в нижнем регистре? Я не понимаю, почему один будет более оптимальным, чем другой.
Параппа

36
Преобразование в верхний регистр, а не в нижний регистр также может предотвратить некорректное поведение в определенных культурах. Например, на турецком языке две строчные буквы i отображаются на одну заглавную букву I. Google "turkish i" для более подробной информации.
Нил

34
Я попытался сравнить ToUpperInvariant с ToLowerInvariant. Я не могу найти никакой разницы в их производительности под .NET 2.0 или 3.5. Конечно, нет ничего, что оправдывало бы «настоятельно рекомендовать» использование одного над другим.
Расмус Фабер

19
ToUpperInvariant является предпочтительным, потому что он делает все символы в обоих направлениях. См. Msdn.microsoft.com/en-us/library/bb386042.aspx . Для сравнения напишите"a".Equals("A", StringComparison.OrdinalIgnoreCase)
SLaks

408

Мой любимый трюк - использование оператора null coalesce и круглых скобок для автоматического создания экземпляров коллекций для меня.

private IList<Foo> _foo;

public IList<Foo> ListOfFoo 
    { get { return _foo ?? (_foo = new List<Foo>()); } }

23
Тебе трудно читать?
Рири

72
Его немного трудно читать для неопытного, неопытного. Но он компактен и содержит пару паттернов и языковых функций, которые программист должен знать и понимать. Так что, хотя на первый взгляд это трудно, это дает вам возможность учиться.

38
Ленивая инстанциация - это своего рода халатность, потому что это плохой выбор, чтобы избегать думать о классовых инвариантах. У этого также есть проблемы параллелизма.
Джон Лейдгрен

17
Мне всегда сообщали, что это плохая практика, что вызов свойства не должен делать что-то как таковое. Если бы у вас там был набор и было установлено значение null, было бы очень странно для тех, кто использует ваш API.
Ян

8
@Joel за исключением того, что установка для ListOfFoo значения null не является частью классового контракта и не является хорошей практикой. Вот почему нет сеттера. Это также, почему ListOfFoo гарантированно возвращает коллекцию и никогда не нуль. Это просто очень странная жалоба на то, что если вы сделаете две плохие вещи (сделаете установщик и установите коллекцию равной нулю), это приведет к тому, что ваши ожидания окажутся неверными. Я бы не советовал делать Environment.Exit () в геттере. Но это также не имеет ничего общего с этим ответом.

315

Избегайте проверки на нулевые обработчики событий

Добавление пустого делегата к событиям при объявлении, устранение необходимости всегда проверять событие на null, прежде чем вызывать его, - это здорово. Пример:

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");
    }
}

Пожалуйста, также посмотрите эту связанную дискуссию и этот блог Эрика Липперта на эту тему (и возможные недостатки).


87
Я считаю, что проблема возникнет, если вы будете полагаться на эту технику, а затем вам придется сериализовать класс. Вы исключите событие, а затем при десериализации вы получите NullRefference ..... Так что можно просто придерживаться «старого способа» ведения дел. Это безопаснее.
Sirrocco

16
вы все еще можете установить свой обработчик событий на нуль, так что вы все равно можете получить нулевую ссылку, и у вас все еще есть состояние гонки.
Роберт Полсон

64
Быстрый тест профиля показывает, что обработчик событий с фиктивной подпиской без нулевого теста занимает примерно в 2 раза больше времени, чем обработчик неподписанных событий с нулевым тестом. Обработчик многоадресных событий без нулевого теста занимает примерно в 3,5 раза больше времени, чем обработчик одноадресных событий с нулевым тестом.
P Daddy

54
Это избавляет от необходимости нулевой проверки, просто всегда имея собственного подписчика. Даже как пустое событие, это несет накладные расходы, которые вы не хотите. Если нет подписчиков, вы вообще не хотите запускать событие, не всегда сначала запускайте пустое фиктивное событие. Я бы посчитал этот плохой код.
Кит

56
Это ужасное предложение по причинам, приведенным в комментариях выше. Если вам нужно, чтобы ваш код выглядел «чистым», используйте метод расширения, чтобы проверить наличие нуля, затем вызовите событие. Кто-то с правами на изменение должен обязательно добавить минусы к этому ответу.
Грег

305

Все остальное плюс

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();

14
в # 3 вы можете сделать Enumerable.Range (1,5)
Echostorm

18
я думаю, что вы смогли инициализировать массивы с помощью int [] nums = {1,2,3}; начиная с версии 1.0 :) даже не нужно ключевое слово "new"
Lucas

13
также лямбда без параметров () => DoSomething ();
Пабло Ретик

5
Я использовал оба {получить; внутренний набор; } и получить; защищенный набор; }, так что этот шаблон является последовательным.
Кит

7
@Kirk Broadhurst - вы правы - new web.Control()также будет работать в этом примере. В ::синтаксических заставляет его лечить префикс как псевдоним пространства имен, так что вы могли бы иметь класс с именем webи web::Controlсинтаксис будет по- прежнему работать, в то время как web.Controlсинтаксис не будет знать , проверять ли класс или пространство имен. Из-за этого я обычно использую ::псевдонимы пространства имен.
Кит

286

Я давно не знал ключевое слово "как".

MyClass myObject = (MyClass) obj;

против

MyClass myObject = obj as MyClass;

Вторая вернет ноль, если obj не является MyClass, а не сгенерирует исключение приведения класса.


42
Не переусердствуйте, хотя. Многие люди, кажется, используют это потому, что предпочитают синтаксис, хотя им нужна семантика (ToType) x.
Скотт Лэнгхэм

4
Я не верю, что он предлагает лучшую производительность. Вы профилировали это? (Очевидно, что это происходит, когда приведение не удается ... но когда вы используете приведение (MyClass), сбои являются исключительными ... и крайне редкими (если они вообще случаются), поэтому это не имеет значения.
Скотт Лэнгхэм,

7
Это только более эффективно, если в обычном случае происходит сбой приведения. В противном случае объект прямого приведения (типа) работает быстрее. Для прямого броска требуется больше времени, чтобы вызвать исключение, чем для возврата нулевого значения.
Спенс

15
Прямо по тем же строкам ключевого слова «как» ... ключевое слово «есть» так же полезно.
dkpatt

28
Вы можете использовать его и получить исключение NullReferenceException в будущем, если раньше у вас могло быть InvalidCastException.
Андрей Ринея

262

Две вещи, которые мне нравятся, это свойства 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()}

6
Следует ли отметить, что автоматические свойства являются только функцией C # 3.0?
Джаред Апдайк

21
Автоматические свойства были введены с компилятором 3.0. Но поскольку компилятор может быть настроен на вывод кода 2.0, он работает просто отлично. Только не пытайтесь скомпилировать код 2.0 с автоматическими свойствами в старом компиляторе!
Джошуа Шеннон

74
Многие люди не понимают, что get и set могут иметь различную доступность, например: public string Name {get; частный сет;}
Надер Ширази

7
Единственная проблема с автоматическими свойствами заключается в том, что невозможно предоставить значение инициализации по умолчанию.
Стейн Осмул

7
@ ANeves: нет, это не так. С этой страницы: DefaultValueAttribute не приведет к автоматической инициализации элемента со значением атрибута. Вы должны установить начальное значение в вашем коде. [DefaultValue]используется для дизайнера, поэтому он знает, показывать ли свойство жирным шрифтом (то есть не по умолчанию).
Роджер Липскомб

255

Ключевое слово «default» в универсальных типах:

T t = default(T);

приводит к нулю, если T является ссылочным типом, и 0, если это int, и false, если это логическое значение, и так далее.


4
Плюс: тип? как ярлык для Nullable <тип>. default (int) == 0, но default (int?) == null.
Солнце


221

Символ @ указывает компилятору игнорировать любые escape-символы в строке.

Просто хотел уточнить это ... он не говорит ему игнорировать escape-символы, он на самом деле говорит компилятору интерпретировать строку как литерал.

Если у тебя есть

string s = @"cat
             dog
             fish"

он будет фактически распечатан как (обратите внимание, что он даже включает пробелы, используемые для отступа):

cat
             dog
             fish

Разве строка не будет включать все пробелы, которые вы использовали для отступа?
Энди

18
Да, это называется дословной строкой.
Джоан Венге

4
Было бы яснее, если бы в выходных данных были указаны пробелы, которые также будут распечатаны. Прямо сейчас кажется, что символы новых строк печатаются, но пробелы игнорируются.
aleemb

Очень полезно для экранирования регулярных выражений и длинных SQL-запросов.
ashes999

Он также отображает {{на {и }}на что }делает его полезным для string.Format().
Ферруччо

220

Я думаю, что одной из наиболее недооцененных и менее известных функций 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 прямо сейчас.


3
У меня есть вспомогательный класс отражения, который делает то же самое с FieldInfo, PropertyInfo и т. Д.
Olmo

Да, это здорово. Я смог использовать такие методы для написания кода, как EditValue(someEmployee, e => e.FirstName);в моей бизнес-логике, и он автоматически генерировал всю логику для ViewModel и View для редактирования этого свойства (так, метка с текстом «Имя» и TextBox с привязка, которая вызывает установщик свойства FirstName, когда пользователь редактирует имя и обновляет представление с использованием метода получения). Это, кажется, основа для большинства новых внутренних DSL в C #.
Скотт Уитлок

4
Я думаю, что они не столько менее известны, сколько менее понятны.
Джастин Морган

Зачем вам нужно регистрировать метод? Я никогда не использовал это раньше - где и когда это будет использоваться?
MoonKnight

209

" доходность " пришла бы мне в голову. Некоторые из атрибутов, таких как [DefaultValue ()] , также среди моих любимых.

Ключевое слово " var " немного более известно, но его можно использовать и в приложениях .NET 2.0 (если вы используете компилятор .NET 3.5 и устанавливаете его для вывода кода 2.0), кажется не очень известным хорошо.

Редактировать: kokos, спасибо за указание ?? оператор, это действительно очень полезно. Так как в нем сложно найти Google (так как ?? просто игнорируется), вот страница документации MSDN для этого оператора: ?? Оператор (C # Reference)


14
В документации по умолчанию указано, что на самом деле значение свойства не установлено. Это всего лишь помощник для визуализаторов и генераторов кода.
Борис Калленс

2
Что касается DefaultValue: тем временем некоторые библиотеки используют его. ASP.net MVC использует DefaultValue для параметров действия контроллера (что очень полезно для необнуляемых типов). Строго говоря, конечно, это генератор кода, поскольку значение устанавливается не компилятором, а кодом MVC.
Майкл Стум

6
Название для оператор - оператор "Null Coalescing"
Armstrongest,

yield - мой любимый, хотя оператор коалесценции там. Я не вижу ничего о CAS или подписи сборок, строгих именах, GAC ... Полагаю, CAS - это только C # ... но многие разработчики не имеют ни малейшего представления о безопасности.
BrainSlugs83

198

Я склонен находить, что большинство разработчиков на C # не знают о «обнуляемых» типах. В основном, примитивы, которые могут иметь нулевое значение.

double? num1 = null; 
double num2 = num1 ?? -100;

Установите обнуляемую двойные, num1 , в нуль, а затем установить регулярную двойные, 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. Проверьте эту ссылку для более подробной информации


1
DateTime не может быть установлен в нуль.
Джейсон Джексон

2
Так что же, "int" - это синтаксический сахар C # для System.Int32? На самом деле существует поддержка компилятора, основанная на типах Nullable, позволяющая, например, установить для них значение null (что невозможно сделать с помощью только общих структур) и поместить их в качестве основного типа.
P Daddy

4
@P Папа - да, int - это синтаксический сахар для System.Int32. Они полностью взаимозаменяемы, как и int? === Int32? === Nullable <Int32> === Nullable <int>
cjk

6
@ck: Да, int - это псевдоним System.Int32, как T? это псевдоним для Nullable <T>, но это не просто синтаксический сахар. Это определенная часть языка. Псевдонимы не только облегчают чтение кода, но и лучше соответствуют нашей модели. Выражаете Nullable <Double> как двойной? подчеркивает перспективу, что содержащееся таким образом значение является (или может быть) двойным, а не просто какой-то общей структурой, созданной для типа Double. (продолжение)
P Daddy

3
... В любом случае, аргумент был не о псевдонимах (это была просто плохая аналогия, я полагаю), но что обнуляемые типы - используя любой синтаксис, который вы предпочитаете - действительно являются языковой функцией, а не продуктом обобщений. Вы не можете воспроизвести все функциональные возможности значений NULL (сравнение с / присваивание NULL, переадресация операторов, бокс базового типа или NULL, совместимость с NULL-объединением и asоператорами) с помощью одних обобщений. Одно только Nullable <T> является рудиментарным и далеко не звездным, но концепция обнуляемых типов как части языка - это задница.
P Папа

193

Вот некоторые интересные скрытые возможности C # в форме недокументированных ключевых слов C #:

__makeref

__reftype

__refvalue

__arglist

Это недокументированные ключевые слова C # (даже Visual Studio распознает их!), Которые были добавлены для более эффективной упаковки / распаковки до использования обобщений. Они работают в координации со структурой System.TypedReference.

Также есть __arglist, который используется для списков параметров переменной длины.

Одна вещь, о которой мало кто знает, это System.WeakReference - очень полезный класс, который отслеживает объект, но все же позволяет сборщику мусора собирать его.

Наиболее полезной «скрытой» функцией будет ключевое слово yield return. Это на самом деле не скрыто, но многие люди не знают об этом. LINQ построен на этом; он позволяет выполнять запросы с задержкой, генерируя конечный автомат. Раймонд Чен недавно опубликовал информацию о внутренних мелочах .


2
Подробнее о недокументированных ключевых слов Питера Бромберга . Я до сих пор не понимаю, есть ли причины их использовать.
HuBeZa

@HuBeZa с появлением дженериков, не так много (каких?) Веских причин использовать __refType, __makeref и __refvalue. Они были использованы в первую очередь, чтобы избежать бокса до дженериков в .NET 2.
Иуда Габриэль Химанго

Мэтт, с появлением обобщений в .NET 2, было мало оснований для использования этих ключевых слов, поскольку их целью было избежать использования бокса при работе с типами значений. См. Ссылку HuBeZa, а также см. Codeproject.com/Articles/38695/UnCommon-C-keywords-A-Look#ud
Иуда Габриэль Химанго

185

Союзы (вид разделяемой памяти 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;


7
@ Джордж, творит чудеса, когда вы общаетесь с устаревшими приложениями через сокеты с помощью объявлений c union.
Скотт

2
Также имеет смысл сказать int и с плавающей точкой со смещением 0. Это то, что вам нужно, если вы хотите манипулировать числами с плавающей запятой в виде битовых масок, что иногда требуется. Особенно, если вы хотите узнать что-то новое о числах с плавающей запятой.
Джон Лейдгрен

2
Раздражает то, что если вы собираетесь использовать эту структуру, компилятор заставит вас установить ВСЕ переменные в функции init. Так что если у вас есть: public A (int int32) {Int32 = int32; } он выдаст «Поле 'One' должно быть полностью назначено до того, как управление будет возвращено вызывающей стороне", поэтому вы должны установить One = Two = Three = Four = 0; также.
Manixrock

2
Это имеет свое применение, в основном с двоичными данными. Я использую структуру «Pixel» с int32 @ 0 и четырьмя байтами для четырех компонентов @ 0, 1, 2 и 3. Отлично подходит для быстрой и простой обработки данных изображения.
snarf

57
Предупреждение: этот подход не учитывает порядок байтов. Это означает, что ваш код C # не будет работать одинаково на всех машинах. На процессорах с прямым порядком байтов (которые сначала сохраняют младший значащий байт) будет использоваться указанное поведение. Но на процессорах с прямым порядком байтов байты получаются обратными от того, что вы ожидали. Остерегайтесь того, как вы используете это в производственном коде - ваш код может быть не переносимым на определенные мобильные устройства и другое оборудование и может ломаться неочевидными способами (например, два файла якобы в одном формате, но фактически с обратным порядком байтов).
Рэй Бернс

176

Использование @ для имен переменных, которые являются ключевыми словами.

var @object = new object();
var @string = "";
var @if = IpsoFacto(); 

38
Почему вы хотите использовать ключевое слово в качестве имени переменной? Мне кажется, что это сделает код менее читаемым и запутанным.
Джон

41
Ну, причина этого в том, что CLI требует его для взаимодействия с другими языками, которые могут использовать ключевые слова C # в качестве имен членов
Mark Cidade

69
Если вы когда-нибудь хотели использовать HTML-помощники MVC asp.net и определить класс HTML, вы будете рады узнать, что вы можете использовать @class, чтобы он не распознавался как ключевое слово класса
Борис Калленс,

31
Отлично подходит для методов расширения. public static void DoSomething (этот бар @this, строка foo) {...}
Джонатан С. Дикинсон

45
@zihotki: Неверно. var a = 5; ЕЫпе (@a); Prints 5
SLaks

168

Если вы хотите выйти из вашей программы без вызова каких-либо блоков finally или финализаторов, используйте FailFast :

Environment.FailFast()

12
Обратите внимание, что этот метод также создает дамп памяти и записывает сообщение в журнал ошибок Windows.
RickNZ

1
Следует отметить, что при использовании этого метода не будут вызываться финализаторы или события выгрузки домена приложения ...
AwkwardCoder

1
+1 за подсказку, но это не C #, это BCL .NET.
Авель

System.Diagnostics.Process.GetCurrentProcess (). Kill () быстрее
Толгахан Альбайрак

153

Возврат анонимных типов из метода и доступ к членам без отражения.

// 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;
}

42
Это действительно ничего вам не дает. Это на самом деле опасно. Что если GetUserTuple изменен так, чтобы возвращать несколько типов? Приведение не удастся во время выполнения. Одна из замечательных особенностей C # /. Net - проверка времени компиляции. Было бы намного лучше просто создать новый тип.
Джейсон Джексон

9
@ Джейсон Я сказал, что это, вероятно, бесполезно, но это удивительно (и я подумал, что скрыто).
Денис Филлипс

31
Хотя это круто, это кажется довольно неудачным выбором дизайна. Вы в основном определили анонимный тип в двух местах. В этот момент просто объявите реальную структуру и используйте ее напрямую.
Пол Александр

6
@ Джордж: такое соглашение будет называться ... структура?
Р. Мартиньо Фернандес

2
этот прием называется «cast by sample», и он не будет работать, если метод, который возвращает анонимный тип, находится в другой сборке.
Desco

146

Вот полезный пример для регулярных выражений и путей к файлам:

"c:\\program files\\oldway"
@"c:\program file\newway"

Символ @ указывает компилятору игнорировать любые escape-символы в строке.


27
Кроме того, константа @ принимает новые строки внутри. Идеально подходит при назначении многострочного сценария в строку.
Тор Хауген

11
Не забывайте также избегать кавычек, просто удвойте их, другими словами. [code] var candy = @ "Мне нравятся" "красные" "леденцы."; [/ code]
Дэйв

5
Я склонен строить пути с помощью Path.Combine. Я определенно использую @ для регулярных выражений!
Дэн Макклейн,

6
@new также является переменной вместо ключевого слова: @this, @int, @return, @interface ... и так далее :)
IAbstract

Это никуда не денется: но каковы наиболее скрытые функции или уловки C #, которые даже фанаты C #, наркоманы, эксперты едва знают?
publicgk

141

Примеси. По сути, если вы хотите добавить функцию к нескольким классам, но не можете использовать один базовый класс для всех них, попросите каждый класс реализовать интерфейс (без членов). Затем напишите метод расширения для интерфейса , т.е.

public static DeepCopy(this IPrototype p) { ... }

Конечно, некоторая ясность принесена в жертву. Но это работает!


4
Да, я думаю, что это реальная сила методов расширения. Они в основном допускают реализации методов интерфейса.
Джон Бубриски

Это также удобно, если вы используете NHibernate (или Castle ActiveRecord) и вам нужно использовать интерфейсы для своих коллекций. Таким образом, вы можете задать поведение для интерфейсов коллекции.
Райан Ланди

Это, в основном, как все методы LINQ реализованы, для записи.
WCWedin

Вот ссылка, которая говорит о том, что я упоминал выше, используя методы расширения с интерфейсами сбора в NHibernate или Castle ActiveRecord: devlicio.us/blogs/billy_mccafferty/archive/2008/09/03/…
Райан Ланди,

7
Если бы только они допускали свойства расширения !!!! Я терпеть не мог писать метод расширения, который просил быть доступным только для чтения ....
Джон Гибб

130

Не уверен, почему кто-то захочет использовать Nullable <bool>. :-)

Верно, Неверно , FileNotFound ?


87
если ожидается, что пользователь ответит на вопрос «да нет», тогда будет уместно использовать
ноль,

22
Обнуляемые типы удобны для взаимодействия с базой данных, где столбцы таблицы часто обнуляются.
Tuinstoel

11
Да нет, может быть?
Дэн Блэр

21
Сохраните значения ThreeState CheckBox
Shimmy Weitzhandler

8
Как в SQL: да / нет / неизвестно.
erikkallen

116

Этот не "спрятан" так, как его неправильно называют.

Большое внимание уделяется алгоритмам «карта», «уменьшить» и «фильтр». Большинство людей не осознают, что в .NET 3.5 добавлены все три из этих алгоритмов, но он дал им очень SQL-имена, основываясь на том факте, что они являются частью LINQ.

"карта" => Выбрать
Преобразует данные из одной формы в другую.

"уменьшить" => Агрегировать
агрегирует значения в один результат

"filter" => Где
Фильтрует данные на основе критериев

Возможность использовать LINQ для выполнения встроенной работы с коллекциями, которые использовались для итераций и условных выражений, может быть невероятно полезной. Стоит узнать, как все методы расширения LINQ могут сделать ваш код намного более компактным и обслуживаемым.


1
Выбор также действует как функция возврата в монадах. См. Stackoverflow.com/questions/9033/hidden-features-of-c#405088
Марк Сидаде

1
Использование «select» необходимо, только если вы используете синтаксис SQLish. Если вы используете синтаксис метода расширения - someCollection.Where (item => item.Price> 5.99M) - использование операторов выбора не требуется.
Брэд Уилсон

7
@Brad, это (где) является операцией фильтра. Попробуйте выполнить операцию с картой без выбора ...
Eloff

2
По моему мнению, LINQ - это большая вещь, которая произошла с C #: stackoverflow.com/questions/2398818/…
Лениэль Маккаферри

1
Наименьшая сигнатура агрегата - это функция «уменьшить», средняя сигнатура агрегата - гораздо более мощная функция «складывания»!
Бретт Видмайер

115
Environment.NewLine

для системных независимых строк.


10
Раздражает тот факт, что он не входит в компактную структуру.
Stormenet

14
Стоит отметить, что это относится к хост-платформе приложения - поэтому, если вы создаете данные, предназначенные для другой системы, вы должны использовать \ n или \ r \ n соответственно.
Mesh

Это часть BCL .NET, а не особенность C # как таковая.
Авель

111

Если вы пытаетесь использовать фигурные скобки внутри выражения 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"

19
@Kyralessa: На самом деле, да, это фигурные скобки, но «фигурные скобки» - это альтернативное название для них. [и ]являются квадратными скобками, и <и >являются угловыми скобками. См. En.wikipedia.org/wiki/Bracket .
icktoofay

4
Ты прав. Но когда я прокомментировал, там не было «фигурных» скобок.
Райан Ланди

2
Очень приятно отметить. Помните, мой первый String.Format выглядел так: String.Format ("{0} я в фигурных скобках {1} {2} {3}", "{", "}", 3, "слепые мыши")); Однажды я узнал, что избавиться от них можно с помощью {{и}}, я был так счастлив :)
Гертьян,

Муахахаха ... Программирую уже 3 года в .Net, а я этого не знал. : D
Арнис Лапса

«фигурные» скобки, действующие аналогично escape-последовательностям
Пратик

104
  1. ?? - оператор коалесцирования
  2. using ( оператор / директива ) - отличное ключевое слово, которое можно использовать не только для вызова Dispose
  3. только для чтения - следует использовать больше
  4. сетевые модули - очень плохо, нет поддержки в Visual Studio

6
using может также использоваться для псевдонима длинного пространства имен для более удобной строки, то есть: using ZipEncode = MyCompany.UtilityCode.Compression.Zip.Encoding; Здесь есть еще: msdn.microsoft.com/en-us/library/sf0df423.aspx
Дейв Р.

2
Это действительно не то же самое. При вызове Dispose вы можете использовать оператор using, когда для псевдонимов типов вы используете директиву using.
Ойвинд Скаар

2
На всякий случай, если вы хотите имя # 1 (как вы сделали с троичным оператором), ?? называется нулевым оператором слияния.
Дж. Стин

4
@LucasAardvark: как упомянул Дж. Стин, он называется оператором нулевого слияния. Ищите это!
Кокос

1
Искать ?? оператор в Google try: google.com/search?q=c%23+%3F%3F+operator
backslash17

103

@ Эд, я немного неохотно пишу, потому что это немного больше, чем придирки. Однако я хотел бы отметить, что в вашем примере кода:

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, чтобы быть уверенным, но мои инстинкты говорят мне, что это сократит не более двух операций приведения типов до максимум одной. Вряд ли в любом случае сломать процессинговый банк; лично я думаю, что последняя форма тоже выглядит чище.


16
Если приведение относится к точному типу (приведение к «A», когда объект является «A», а не производным от него), прямое приведение происходит в ~ 3 раза быстрее, чем «as». При приведении к производному типу (приведение к «A», когда объект - «B», что происходит от «A»), прямое приведение на ~ 0.1x медленнее, чем «as». «есть», то «как» просто глупо.
P Daddy

2
Нет, но вы можете написать «if ((c = obj as MyClass)! = Null)».
Дейв Ван ден Эйнде

10
isи asне будет делать приведения пользователя. Таким образом, вышеприведенный код спрашивает у isоператора, является ли объект obj производным от MyClass (или имеет неявную приведенную систему приведения). Кроме того, isне удается null. Оба этих крайних случая могут быть важны для вашего кода. Например, вы можете написать: « if( obj == null || obj is MyClass ) c = (MyClass)obj; Но это строго отличается от: try { c = (MyClass)obj; } catch { }так как первый не будет выполнять какие-либо определенные пользователем преобразования, но последний будет выполнять. Без nullпроверки первый также не будет установлен, cкогда objесть null.
Адам Лютер

2
В IL приведение идет к CASTCLASS, но as / is идет к инструкции ISINST.
Джон Гибб

5
Я выполнил тест для этого, приведя IEnumerable<int>к List<int>и casting object( new object()) IEnumerable<int>, чтобы убедиться, что ошибок нет: прямое приведение: 5.43ns, is-> as приведение: 6.75ns, приведение: 5.69ns. Затем тестирование неверных приведений: прямое приведение: 3125000 нс, приведение: 5,41 нс. Вывод: перестаньте беспокоиться о факторе 1%, и просто убедитесь, что вы используете /, поскольку приведение может быть недопустимым, потому что исключения (также если они обрабатываются) ОЧЕНЬ медленны по сравнению с приведением, мы говорим о факторе 578000 медленнее. Помните, что последняя часть, остальное не имеет значения (.Net FW 4.0, выпуск сборки)
Aidiakapi

98

Возможно, не продвинутая техника, но та, которую я вижу все время, которая сводит меня с ума:

if (x == 1)
{
   x = 2;
}
else
{
   x = 3;
}

может быть сжат до:

x = (x==1) ? 2 : 3;

10
Тройной оператор был запрещен в моей группе. Некоторым это просто не нравится, хотя я никогда не понимал почему.
Джастин Р.

13
Я думаю, потому что это не одно и то же, быть оператором, а не утверждением. Я предпочитаю первый подход сам. Он более последовательный и легко расширяемый. Операторы не могут содержать операторы, поэтому в ту минуту, когда вам нужно расширить тело, вам придется преобразовать этот троичный оператор в оператор if
kervin

16
может быть сжат до x ++; Хорошо, это бессмысленно ^^
Франсуа

5
@Guillaume: для учета всех значений x: x = 2 + System.Math.Min (1, System.Math.Abs ​​(x-1));
mbeckish

38
Это на самом деле называется «условным оператором» - это троичный оператор , поскольку он принимает три аргумента. ru.wikipedia.org/wiki/Conditional_operator
Blorgbeard выходит
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.