Скрытые особенности С#?

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

where T : struct

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

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

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

Вот раскрытые возможности:


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

Атрибуты

Синтаксис

  • ?? (coalesce nulls) оператор kokos
  • Знаки числа Ник Берарди
  • where T:new Lars Mæhlum
  • Неявные дженерики Keith
  • Однопараметрические лямбды Keith
  • Авто свойства Keith
  • Имена псевдонимов Keith
  • Вербатные строковые литералы с @by Patrick
  • enum значения lfoust
  • @variablenames marxidad
  • event операторы marxidad
  • Форматировать скобки строки Портман
  • Модификаторы доступности доступа к ресурсам по xanadont
  • Условный (тройной) оператор (?:) на JasonS
  • checked и unchecked операторы Binoj Antony Операторы
  • implicit and explicit Флори

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

Возможности Visual Studio

  • Выберите блок текста в редакторе Himadri
  • Фрагменты DannySmurf

Структура

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

Советы и рекомендации

  • Хороший метод для обработчиков событий Andreas H.R. Nilsson
  • Сравнение с верхним регистром John
  • Доступ к анонимным типам без отражения dp
  • Быстрый способ ленивого создания свойств коллекции Будет
  • JavaScript-подобные анонимные встроенные функции roosteronacid

Другие

Ответы

Ответ 1

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

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

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

Ответ 2

lambdas и тип inferrence недооцениваются. Lambdas может иметь несколько операторов, и они удваиваются как совместимый объект-делегат автоматически (просто убедитесь, что подпись соответствует), как в:

Console.CancelKeyPress +=
    (sender, e) => {
        Console.WriteLine("CTRL+C detected!\n");
        e.Cancel = true;
    };

Обратите внимание, что у меня нет new CancellationEventHandler, и я не должен указывать типы sender и e, они выводятся из события. Вот почему это менее громоздко для записи всего delegate (blah blah), в котором также требуется указать типы параметров.

Lambdas не нужно возвращать что-либо, а вывод типа чрезвычайно силен в таком контексте.

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

Ответ 3

Из Рик Стралл:

Вы можете связать?? оператора, чтобы вы могли выполнить нулевое сравнение.

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

Ответ 4

Псевдонимы:

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

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

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

Ответ 5

От CLR через С#:

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

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

Ответ 6

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

private IList<Foo> _foo;

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

Ответ 7

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

Добавление пустого делегата к событиям в объявлении, подавляющее необходимость всегда проверять событие для 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");
    }
}

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

Ответ 8

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

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

Ответ 9

Я не знал ключевое слово "как" довольно долгое время.

MyClass myObject = (MyClass) obj;

против

MyClass myObject = obj as MyClass;

Вторая возвращает null, если obj не является MyClass, а не бросает исключение класса.

Ответ 10

Две вещи, которые мне нравятся, - это автоматические свойства, поэтому вы можете еще больше свернуть свой код:

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

Ответ 11

Ключевое слово default по умолчанию:

T t = default(T);

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

Ответ 12

Атрибуты вообще, но больше всего DebuggerDisplay. Экономит ваши годы.

Ответ 13

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

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

Если у вас

string s = @"cat
             dog
             fish"

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

cat
             dog
             fish

Ответ 14

Я думаю, что одна из самых недооцененных и менее известных функций С# (.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);
    }
}

Это одна из главных причин того, что я сейчас люблю Лямбда и Деревни Expression.

Ответ 15

" yield" придет мне на ум. Некоторые из таких атрибутов, как [DefaultValue()], также относятся к моим фаворитам.

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

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

Ответ 16

Я обычно обнаруживаю, что большинство разработчиков С# не знают о типах "nullable". В принципе, примитивы, которые могут иметь нулевое значение.

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

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

Ответ 17

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

__makeref

__reftype

__refvalue

__arglist

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

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

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

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

Ответ 18

Союзы (тип разделяемой памяти С++) в чистом, безопасном С#

Не прибегая к небезопасному режиму и указателям, вы можете иметь членов класса, разделяющих пространство памяти в классе/структуре. Учитывая следующий класс:

[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;

Ответ 19

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

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

Ответ 20

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

Environment.FailFast()

Ответ 21

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

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

Ответ 22

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

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

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

Ответ 23

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

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

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

Ответ 24

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

Правда, False, FileNotFound?

Ответ 25

Этот не скрыт настолько, насколько это неправильно.

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

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

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

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

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

Ответ 26

Environment.NewLine

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

Ответ 27

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

Ответ 28

  • ? - коалесцирующий оператор
  • используя (statement/директива) - отличное ключевое слово, которое можно использовать не только для вызова Dispose
  • readonly - следует использовать больше
  • netmodules - слишком плохо там нет поддержки в Visual Studio

Ответ 29

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

MyClass c;
  if (obj is MyClass)
    c = obj as MyClass

Если вы собираетесь использовать "is", зачем следить за ним с помощью безопасного броска, используя "как"? Если вы установили, что obj действительно MyClass, стандартное приложение для болота:

c = (MyClass)obj

... никогда не сработает.

Аналогично, вы можете просто сказать:

MyClass c = obj as MyClass;
if(c != null)
{
   ...
}

Я не знаю достаточно об информерах .NET, но мои инстинкты говорят мне, что это сократит максимум до двух типов операций приведения до максимума. Это вряд ли может сломать банк обработки в любом случае; лично, я думаю, что последняя форма выглядит более чистой.

Ответ 30

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

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

можно конденсировать до:

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