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

1476

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

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

Другие

Теги:
hidden-features

294 ответа

752

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

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

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

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

554

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

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

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

От CLR через С#:

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

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

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

Псевдонимы:

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

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

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

389

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

private IList<Foo> _foo;

public IList<Foo> ListOfFoo 
    { get { return _foo ?? (_foo = new List<Foo>()); } }
  • 3
    Теперь это крутая техника: встроить назначение, когда ноль!
  • 23
    Вам не трудно читать?
Показать ещё 21 комментарий
320

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

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();
  • 0
    На самом деле мне нужно было сделать первый в проекте с использованием Anthem, потому что он забивает пространство имен WebControls.
  • 14
    в # 3 вы можете сделать Enumerable.Range (1,5)
Показать ещё 24 комментария
314

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

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

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

  • 0
    Интересный совет! Лично я использую вспомогательные методы расширения из своего проекта ( codeplex.com/thehelpertrinity ), чтобы просто сделать это: Click.Raise (this, "foo");
  • 87
    Я полагаю, что проблема возникнет, если вы будете полагаться на эту технику, а затем вам придется сериализовать класс. Вы исключите событие, а затем при десериализации вы получите NullRefference ..... Так что можно просто придерживаться «старого способа» ведения дел. Это безопаснее.
Показать ещё 24 комментария
300

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

MyClass myObject = (MyClass) obj;

против

MyClass myObject = obj as MyClass;

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

  • 42
    Не переусердствуйте, хотя. Многие люди, кажется, используют это потому, что предпочитают синтаксис, хотя им нужна семантика (ToType) x.
  • 0
    Да, в этом ответе указано только одно из различий между (ToType) и «как ToType».
Показать ещё 10 комментариев
249

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

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()}
  • 0
    Это делает классы намного быстрее писать.
  • 6
    Следует ли отметить, что автоматические свойства являются только функцией C # 3.0?
Показать ещё 11 комментариев
243

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

T t = default(T);

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

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

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

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

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

219

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

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

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

  • 14
    В документации по умолчанию указано, что на самом деле значение свойства не установлено. Это всего лишь помощник для визуализаторов и генераторов кода.
  • 2
    Что касается DefaultValue: тем временем некоторые библиотеки используют его. ASP.net MVC использует DefaultValue для параметров действия контроллера (что очень полезно для необнуляемых типов). Строго говоря, конечно, это генератор кода, так как значение устанавливается не компилятором, а кодом MVC.
Показать ещё 2 комментария
210

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

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

Если у вас

string s = @"cat
             dog
             fish"

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

cat
             dog
             fish
  • 0
    Разве строка не будет включать все пробелы, которые вы использовали для отступа?
  • 18
    Да, это называется дословной строкой.
Показать ещё 3 комментария
193

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

__makeref

__reftype

__refvalue

__arglist

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

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

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

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

  • 2
    Подробнее о недокументированных ключевых слов Питера Бромберга . Я до сих пор не понимаю, есть ли причины их использовать.
  • 0
    @HuBeZa с появлением дженериков, не так много (каких?) Веских причин использовать __refType, __makeref и __refvalue. Они использовались прежде всего для того, чтобы избежать упаковки до обобщений в .NET 2.
Показать ещё 2 комментария
189

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

  • 0
    В чем разница между этим и просто использованием объектов-обёрток для приматов?
  • 0
    некоторые типы обычно обнуляются, как DateTime. Итак, DateTime? это находка
Показать ещё 8 комментариев
185

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

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

[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. Это то, что вам нужно, если вы хотите манипулировать числами с плавающей запятой в виде битовых масок, что иногда требуется. Особенно, если вы хотите узнать что-то новое о числах с плавающей запятой.
Показать ещё 5 комментариев
176

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

var @object = new object();
var @string = "";
var @if = IpsoFacto(); 
  • 38
    Почему вы хотите использовать ключевое слово в качестве имени переменной? Мне кажется, что это сделает код менее читаемым и запутанным.
  • 41
    Ну, причина этого в том, что CLI требует его для взаимодействия с другими языками, которые могут использовать ключевые слова C # в качестве имен членов
Показать ещё 18 комментариев
160

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

Environment.FailFast()
  • 2
    В самом деле? Вы можете сделать это?
  • 12
    Обратите внимание, что этот метод также создает дамп памяти и записывает сообщение в журнал ошибок Windows.
Показать ещё 3 комментария
160

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

// 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
    @ Джейсон Я сказал, что это, вероятно, бесполезно, но это удивительно (и я думал, что скрыто).
Показать ещё 10 комментариев
153

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

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

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

  • 27
    Кроме того, константа @ принимает новые строки внутри. Идеально подходит при назначении многострочного сценария в строку.
  • 11
    Не забывайте также избегать кавычек, просто удвойте их, другими словами. [code] var candy = @ "Мне нравятся" "красные" "леденцы."; [/ code]
Показать ещё 3 комментария
135

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

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

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

  • 4
    Да, я думаю, что это реальная сила методов расширения. Они в основном учитывают реализации интерфейсных методов.
  • 0
    Это также удобно, если вы используете NHibernate (или Castle ActiveRecord) и вам нужно использовать интерфейсы для своих коллекций. Таким образом, вы можете задать поведение для интерфейсов коллекции.
Показать ещё 5 комментариев
125

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

Правда, False, FileNotFound?

  • 87
    если ожидается, что пользователь ответит на вопрос «да нет», тогда будет уместно использовать ноль, если на вопрос не ответили
  • 2
    Я использовал обнуляемый bool при заполнении ddl сотрудников. Активен только = правда. InActive only - false. Активный и Неактивный = ноль
Показать ещё 21 комментарий
117

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

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

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

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

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

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

  • 0
    Выберите заставлять вас всегда возвращать что-то из предоставленного выражения, если карта менее требовательна к этому.
  • 1
    Select также действует как функция возврата в монадах. См. Stackoverflow.com/questions/9033/hidden-features-of-c#405088
Показать ещё 6 комментариев
116

Если вы пытаетесь использовать фигурные скобки внутри выражения 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 .
  • 4
    Ты прав. Но когда я прокомментировал, в нем не было «фигурных» скобок.
Показать ещё 5 комментариев
110
Environment.NewLine

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

  • 10
    Раздражает тот факт, что он не входит в компактную структуру.
  • 14
    Стоит отметить, что это относится к хост-платформе приложения - поэтому, если вы создаете данные, предназначенные для другой системы, вы должны использовать \ n или \ r \ n соответственно.
Показать ещё 1 комментарий
109
  • ? - коалесцирующий оператор
  • используя (statement/директива) - отличное ключевое слово, которое можно использовать не только для вызова Dispose
  • readonly - следует использовать больше
  • netmodules - слишком плохо там нет поддержки в Visual Studio
  • 0
    «using - отличное ключевое слово, которое можно использовать не только для вызова Dispose». Я заинтригован, не могли бы вы остановиться на этом подробнее?
  • 6
    using может также использоваться для псевдонима длинного пространства имен для более удобной строки, то есть: using ZipEncode = MyCompany.UtilityCode.Compression.Zip.Encoding; Здесь есть еще: msdn.microsoft.com/en-us/library/sf0df423.aspx
Показать ещё 6 комментариев
108

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

  • 0
    Вы знаете - хотите верьте, хотите нет - прямой актер медленнее, чем актерский состав. Я не верил в это, пока не измерил это сам.
  • 16
    Если приведение относится к точному типу (приведение к «A», когда объект является «A», а не производным от него), прямое приведение происходит в ~ 3 раза быстрее, чем «as». При приведении к производному типу (приведение к «A», когда объект - «B», что происходит от «A»), прямое приведение на ~ 0.1x медленнее, чем «as». «есть», то «как» просто глупо.
Показать ещё 10 комментариев
102

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

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

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

x = (x==1) ? 2 : 3;
  • 0
    Если я не ошибаюсь, это было с некоторых ранних дней C ++. (Возможно, у Straight C это тоже было, но я не могу вспомнить.)
  • 0
    «Это» называется «троичным оператором». en.wikipedia.org/wiki/Ternary_operation
Показать ещё 16 комментариев
94

Многие люди не понимают, что они могут сравнивать строки, используя: OrdinalIgnoreCase вместо того, чтобы делать someString.ToUpper(). Это устраняет дополнительные накладные расходы на строку.

if( myString.ToUpper() == theirString.ToUpper() ){ ... }

становится

if( myString.Equals( theirString, StringComparison.OrdinalIgnoreCase ) ){ ... }
  • 39
    Это можно изменить довольно легко, чтобы быть также нулевым: var isEqual = String.Equals (a, b, StringComparison.OrdinalIgnoreCase);
  • 0
    Но ... это не особенность C #, это особенность платформы .Net, точнее особенность класса "String"
84

Только что выученные анонимные типы могут вывести имена свойств из имени переменной:

string hello = "world";
var o = new { hello };
Console.WriteLine(o.hello);
  • 0
    это потому, что анонимные типы не являются анонимными после компиляции.
75

Мне нравится искать вещи в списке вроде: -

bool basketContainsFruit(string fruit) {
  return new[] { "apple", "orange", "banana", "pear" }.Contains(fruit);
}

Вместо: -

bool basketContainsFruit(string fruit) {
  return fruit == "apple" || fruit == "orange" || fruit == "banana" ||
    fruit == "pear";
}

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

  • 0
    Мне любопытно, что компилятор выкладывает для обеих реализаций. Первый, безусловно, выглядит чище и это симпатичный способ сделать это. Может быть медленнее, если объект сначала создается в памяти, а затем повторяется.
  • 0
    ты наверное хотел фрукт == банан?
Показать ещё 12 комментариев
73

Я выбрал это при использовании ReSharper:

Неявное преобразование групп методов

//If given this:
var myStrings = new List<string>(){"abc","def","xyz"};
//Then this:
myStrings.ForEach(s => Console.WriteLine(s));
//Is equivalent to this:
myStrings.ForEach(Console.WriteLine);

См. " Преобразование группы неявных методов в С#" для более.

  • 0
    не для Debug.WriteLine, хотя он использует внутреннюю инструкцию #if DEBUG.
  • 0
    @AndyC: Здесь речь идет не об этом (ForEach где-то просто определяется как пользовательский метод расширения. Есть религиозные дебаты по этому поводу в другом месте :))
Показать ещё 1 комментарий
72

Вот новый метод строкового класса в С# 4.0:

String.IsNullOrWhiteSpace(String value)

Это о времени.

  • 3
    В чем проблема с созданием собственного метода util, который возвращает это: (myString ?? "").Trim() == ""
  • 1
    @ Чарли Разве возврат каретки не рассматривается как пробел?
Показать ещё 5 комментариев
72

Честно говоря, эксперты по самому определению должны знать этот материал. Но чтобы ответить на ваш вопрос: Таблица встроенных типов (ссылка на С#)

Знак компилятора для чисел широко известен для них:

Decimal = M
Float = F
Double = D

// for example
double d = 30D;

Однако они более неясны:

Long = L
Unsigned Long = UL
Unsigned Int = U
  • 4
    Каждый раз, когда я имею дело с десятичными числами, я должен посмотреть на m. Это только я или я не очень интуитивен? :)
  • 47
    Синтаксис M происходит от старого типа VB, который называется Money. М == Деньги == Десятичные.
Показать ещё 7 комментариев
71
  • TransactionScope и DependentTransaction в System.Transactions - облегченный способ использования обработки транзакций в .NET - это не только для транзакции базы данных или
  • String.IsNullOrEmpty - это тот, который я удивлен, узнав, что многие разработчики не знают о
  • List.ForEach - итерация через общий список с использованием метода делегата

Есть больше, но это три очевидные вершины моей головы...

  • 0
    Я обнаружил, что TransactionScope активно продвигает транзакции в распределенный режим, который использует DTC. Когда задействуется DTC, вам, вероятно, придется иметь дело с безопасностью DCOM. Я склонен избегать боли, используя нативные транзакции.
  • 17
    Этот List.ForEach быстрее, чем foreach, или for (;;) полностью помешан на этом. ForEach использует делегат метода / функции для реализации поведения. Это, прежде всего, означает худшую локальность кэша, потому что код обычно выполняется дальше (в памяти) от фактического цикла. Во-вторых, все, что вам действительно нужно сделать, чтобы убедиться, что это медленнее, это посмотреть сгенерированный нативный код. В List.ForEach происходит намного больше вещей, чем вы думаете.
Показать ещё 6 комментариев
70

InternalsVisibleTo атрибут - это тот, который не так хорошо известен, но может быть легко доступен в определенные обстоятельства. Это в основном позволяет другой сборке иметь доступ к "внутренним" элементам определяющей сборки.

  • 12
    Я регулярно использую это для написания модульных тестов против внутренних членов другой сборки. Таким образом, модульные тесты могут быть исключены из развертываний.
  • 0
    Это определенно мое лучшее открытие из этой темы. Лучшее решение, которое я нашел самостоятельно, чтобы дать модульным тестам в другой сборке доступ к внутренним методам, - это сделать методы защищенными и написать псевдомодельные классы в моей тестовой сборке, унаследованные от тестируемого класса.
Показать ещё 1 комментарий
67

Dictionary.TryGetValue(клавиша K, выходное значение V)

Работает как чек и входит в один. Вместо <

if(dictionary.ContainsKey(key)) 
{
    value = dictionary[key];
    ...
}

вы можете просто сделать;

if(dictionary.TryGetValue(key, out value)) 
{ ... }

и значение установлено.

  • 19
    Еще одно преимущество TryGetValue заключается в том, что если ваш словарь синхронизирован, условия гонки отсутствуют. По сравнению с ContainsKey, где другой поток может удалить элемент, который вы ищете, между вызовами.
  • 4
    TryGetValue выбрасывает, если ключ нулевой - так много для избежания исключений. Я использую метод расширения TryGetValue2 (), чтобы обойти эту проблему.
Показать ещё 1 комментарий
66

При отладке вы можете ввести $exception в окне Watch\QuickWatch\Immediate и получить всю информацию об исключении текущего фрейма. Это очень полезно, если у вас есть исключения 1-го случая!

66

Условная строка .Format:

Применяет различное форматирование к числу в зависимости от того, является ли число положительным, отрицательным или нулевым.

string s = string.Format("{0:positive;negative;zero}", i);

например.

string format = "000;-#;(0)";

string pos = 1.ToString(format);     // 001
string neg = (-1).ToString(format);  // -1
string zer = 0.ToString(format);     // (0)
  • 2
    Это похоже на выражения reg, очень полезно, но я их тоже не помню. Я работаю с вещами, как указано выше, с лопаткой и подушкой.
  • 0
    Круто, я никогда не знал, что это возможно ... Это где-нибудь задокументировано?
Показать ещё 1 комментарий
64

События действительно являются делегатами под капотом, и любой объект-делегат может иметь к нему несколько функций и выделяться из него с помощью операторов + = и-= соответственно.

События также могут управляться с помощью add/remove, аналогично get/set, за исключением того, что они вызывается, когда используются + = и - =:

public event EventHandler SelectiveEvent(object sender, EventArgs args) 
  { add 
     { if (value.Target == null) throw new Exception("No static handlers!");
       _SelectiveEvent += value;
     }
    remove
     { _SelectiveEvent -= value;
     }
  } EventHandler _SelectiveEvent;
59

Больше функции времени исполнения, но я недавно узнал, что есть два сборщика мусора. Рабочая станция gc и сервер gc. Рабочая станция по умолчанию используется для клиентских версий окон, но сервер работает намного быстрее на многоядерных машинах.


<configuration>
   <runtime>
      <gcServer enabled="true"/>
   </runtime>
</configuration>

Будьте осторожны. Сервер gc требует больше памяти.

  • 0
    Хороший совет. Вы должны переместить этот вопрос в вопрос о скрытой библиотеке базовых классов .NET stackoverflow.com/questions/122784/…
  • 0
    Потрясающие. У меня есть многоядерный веб-сервер; его дополнительное ядро и память были потрачены впустую!
Показать ещё 2 комментария
58

Не забывайте о перейти.

  • 55
    Нет, давайте забудем это. ;)
  • 3
    Нет, давайте злоупотреблять этим, пока не придет царство. ;)
Показать ещё 8 комментариев
57

Другие недоиспользуемые операторы checked и unchecked:

short x = 32767;   // 32767 is the max value for short
short y = 32767;
int z1 =  checked((short)(x + y));   //will throw an OverflowException
int z2 =  unchecked((short)(x + y)); // will return -2
int z3 =  (short)(x + y);            // will return -2
  • 6
    Вместо 32767 и комментария, как насчет short.MaxValue ?
  • 25
    Напоминаю всем, что такое MaxValue!
Показать ещё 1 комментарий
53

Использовать "throw" ; вместо "throw ex" ; для сохранения трассировки стека

Если повторное бросание исключения без добавления дополнительной информации, используйте "throw" вместо "throw ex" . Пустой оператор "throw" в блоке catch будет испускать определенный IL, который повторно генерирует исключение, сохраняя исходную трассировку стека. "throw ex" теряет трассировку стека к исходному источнику исключения.

53

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

class Example
{
    public Example(int value1)
        : this(value1, "Default Value")
    {
    }

    public Example(int value1, string value2)
    {
        m_Value1 = value1;
        m_value2 = value2;
    }

    int m_Value1;
    string m_value2;
}
  • 1
    Хотя я использовал это кросс-классы, я никогда не думал о том, чтобы использовать его в одном классе. Я искал повсюду что-то подобное! Большой!
  • 0
    Замечательно. Я всегда использовал это для вызова базы, но никогда не знал, что вы можете использовать его в одном классе!
Показать ещё 6 комментариев
50

Я просто хотел скопировать этот код без комментариев. Итак, фокус в том, чтобы просто нажать кнопку Alt, а затем выделить прямоугольник, который вам нравится (например, ниже).

protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
    {
        //if (e.CommandName == "sel")
        //{
        //    lblCat.Text = e.CommandArgument.ToString();
        //}
    }

В приведенном выше коде, если я хочу выбрать:

e.CommandName == "sel"

lblCat.Text = e.Comman

Затем я нажимаю клавишу ALt и выбираю прямоугольник и не нужно раскомментировать строки.

Проверьте это.

  • 2
    Интересная особенность Visual Studio, но вопрос касается C #.
  • 11
    +1 В этом вопросе есть раздел «Возможности Visual Studio», где ваш ответ идеально подходит.
Показать ещё 6 комментариев
48

Несколько скрытых функций, с которыми я столкнулся:

  • stackalloc, который позволяет выделять массивы в стеке
  • Анонимные методы без явного списка параметров, которые неявно конвертируются в любой тип делегата с параметрами non-out/ref (очень удобны для событий, как отмечено в более раннем комментарии).
  • Многие люди не знают, что такое события (добавьте/удалите пару методов, например get/set для свойств); полевые события в С# действительно объявляют как переменную, так и событие
  • Операторы == и != могут быть перегружены, чтобы возвращать типы, отличные от bool. Странно, но верно.
  • Трансляция выражения запроса в С# 3 действительно "прост" в некотором роде - это означает, что вы можете заставить ее сделать очень странные вещи.
  • У Nullable типов есть специальное поведение в боксе: нулевое значение попадает в нулевую ссылку, и вы также можете отменить ненулевое значение с нулевым типом.
  • 1
    К сожалению, stackalloc требует небезопасного контекста.
47

Ключевое слово params, т.е.

public void DoSomething(params string[] theStrings)
{
  foreach(string s in theStrings)
  {
    // Something with the Strings…
  }
}

Вызывается как

DoSomething("The", "cat", "sat", "on", "the" ,"mat");
47

Несколько вещей, которые мне нравятся:

-Если вы создаете интерфейс, похожий на

 public interface SomeObject<T> where T : SomeObject<T>, new()

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

- использование анонимных типов для создания полезного объекта "на лету":

var myAwesomeObject = new {Name="Foo", Size=10};

-Наконец, многие разработчики Java знакомы с синтаксисом вроде:

public synchronized void MySynchronizedMethod(){}

Однако в С# это недопустимый синтаксис. Обходной путь - это атрибут метода:

 [MethodImpl(MethodImplOptions.Synchronized)]
 public void MySynchronizedMethod(){}
  • 2
    Это все хорошие идеи. Этот сайт обычно предпочитает одну идею за ответ, поэтому они могут оцениваться индивидуально. Я бы дал вам три оценки :)
  • 7
    [MethodImpl (MethodImplOptions.Synchronized)] = блокировка (это) = плохо
Показать ещё 3 комментария
45

@Давид в Дакоте:

Console.WriteLine( "-".PadRight( 21, '-' ) );

Я использовал это, пока не обнаружил, что класс String имеет конструктор, который позволяет вам сделать то же самое более чистым способом:

new String('-',22);
  • 0
    Почему бы не сделать это Console.WriteLine ("" .PadRight (22, '-'));
  • 0
    Чтобы добиться более короткого и чистого кода, например.
44

Статические конструкторы.

Экземпляры:

public class Example
{
    static Example()
    {
        // Code to execute during type initialization
    }

    public Example()
    {
        // Code to execute during object initialization
    }
}

Статические классы:

public static class Example
{
    static Example()
    {
        // Code to execute during type initialization
    }
}

MSDN говорит:

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

Например:

public class MyWebService
{
    public static DateTime StartTime;

    static MyWebService()
    {
        MyWebService.StartTime = DateTime.Now;
    }

    public TimeSpan Uptime
    {
        get { return DateTime.Now - MyWebService.StartTime; }
    }
}

Но вы могли бы так же легко сделать:

public class MyWebService
{
    public static DateTime StartTime = DateTime.Now;

    public TimeSpan Uptime
    {
        get { return DateTime.Now - MyWebService.StartTime; }
    }
}

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

MSDN предлагает полезные заметки о статических конструкторах:

  • Статический конструктор не принимает модификаторы доступа или не имеет параметров.

  • Статический конструктор вызывается автоматически для инициализации класса до создания первого экземпляра
    или любые статические члены ссылаются.

  • Статический конструктор нельзя вызвать напрямую.

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

  • Типичное использование статических конструкторов - это когда класс используя файл журнала и конструктор используется для записи
    записи в этот файл.

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

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

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

  • 0
    Не нужно блокировать, так как все статические элементы являются потокобезопасными.
  • 0
    На самом деле был внутренний веб-сервис, который также можно использовать для изменения значения connectionString. Значит, замок был нужен, нет? Я не снял блокировку, потому что я просто скопировал код, а затем добавил комментарий.
Показать ещё 4 комментария
44

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

  • 18
    Эй, это не совсем так. Ключевое слово volatile указывает компилятору генерировать забор при каждом чтении из поля и забор при каждой записи в поле. Заградительный захват предотвращает перемещение других операций чтения / записи перед забором; ограждение освобождения препятствует перемещению других операций чтения / записи после ограждения. Подробнее читайте здесь: bit.ly/hycbVI
44

Foreach использует Duck Typing

Перефразируя или бесстыдно крадя из Krzysztof Cwalinas blog об этом. Более интересные мелочи, чем что-либо.

Для поддержки вашего объекта foreach вам не нужно реализовывать IEnumerable. То есть это не ограничение и не проверяется компилятором. Проверено, что

  • Ваш объект предоставляет общедоступный метод GetEnumerator, который
    • не принимает параметров
    • возвращает тип, состоящий из двух элементов
      • безпараметрический метод MoveNext, который возвращает логическое
      • свойство Current with getter, возвращающее объект

Например,

class Foo
{
    public Bar GetEnumerator() { return new Bar(); }

    public struct Bar
    {
        public bool MoveNext()
        {
            return false;
        }

        public object Current
        {
            get { return null; }
        }
    }
}

// the following complies just fine:
Foo f = new Foo();
foreach (object o in f)
{
    Console.WriteLine("Krzysztof Cwalina da man!");
}
  • 4
    Это потрясающе! Я не знал, что ... Однако то же самое с инициализаторами коллекции ... все, что вам нужно, это метод Add(x) . public class MyList{ public void Add(string s){} } . Затем вы можете сделать var l = new MyList{"a", "b", "c"}; ...
  • 0
    @JohnGibb: одного только метода Add недостаточно. Из раздела 7.6.10.3 спецификации C #: «Объект коллекции, к которому применяется инициализатор коллекции, должен относиться к типу, который реализует System.Collections.IEnumerable или возникает ошибка времени компиляции».
39

С# + CLR:

  • Thread.MemoryBarrier: Большинство людей не использовало бы его, и в MSDN есть некоторая неточная информация. Но если вы знаете intricacies, вы можете сделать отличную синхронизацию без блокировки.

  • volatile, Thread.VolatileRead, Thread.VolatileWrite: Очень мало людей, которые пользуются этим и даже меньше, кто понимает все риски, которых они избегают и вводят:).

  • ThreadStatic переменные: за последние несколько лет была только одна ситуация, и я обнаружил, что переменные ThreadStatic были абсолютно посланы и необходимы. Например, если вы хотите что-то сделать для всей цепочки вызовов, они очень полезны.

  • fixed keyword: это скрытое оружие, когда вы хотите сделать доступ к элементам большого массива почти так же быстро, как С++ (по умолчанию С# принудительно привязывает проверки, что замедляет работу).

  • default(typeName) ключевое слово может использоваться и вне родового класса. Полезно создать пустую копию структуры.

  • Одна из удобных функций, которые я использую, DataRow[columnName].ToString() всегда возвращает ненулевое значение. Если значение в базе данных было NULL, вы получаете пустую строку.

  • Использовать объект Debugger для автоматического разрыва, если вы хотите привлечь внимание разработчика, даже если он/она не включили автоматическое прерывание исключения:


#if DEBUG  
    if (Debugger.IsAttached)  
        Debugger.Break();  
#endif
  1. Вы можете использовать сложные сложные уродливые общие типы, поэтому вам не нужно копировать их снова и снова. Также вы можете вносить изменения в этот тип в одном месте. Например,

    using ComplicatedDictionary = Dictionary<int, Dictionary<string, object>>;
    ComplicatedDictionary myDictionary = new ComplicatedDictionary();
  • 0
    Сладкие, отличные советы, в последнем случае у вас возникла проблема с тегами ... замените <на & lt; и мы сможем это прочитать :)
  • 0
    Теперь большая часть этого сводится к тому, что «люди не знают механизма, стоящего за многопоточностью» ИМХО. Это может быть правдой, но не приближается к «скрытым языковым возможностям»? Недурно для Debugger.Break ()
39

Затворы

Поскольку анонимные делегаты были добавлены в 2.0, мы смогли разработать закрытие. Они редко используются программистами, но предоставляют большие преимущества, такие как немедленное повторное использование кода. Рассмотрим этот фрагмент кода:

bool changed = false;

if (model.Prop1 != prop1)
{
    changed = true;
    model.Prop1 = prop1;
}
if (model.Prop2 != prop2)
{
    changed = true;
    model.Prop2 = prop2;
}
// ... etc. 

Обратите внимание, что приведенные выше if-инструкции выполняют аналогичные фрагменты кода, за исключением одной строки кода, т.е. устанавливают разные свойства. Это можно укоротить следующим: где переменная строка кода вводится как параметр в объект Action, соответствующий названию setAndTagChanged:

bool changed = false;
Action<Action> setAndTagChanged = (action) => 
{ 
    changed = true; 
    action(); 
};

if (model.Prop1 != prop1) setAndTagChanged(() => model.Prop1 = prop1);
if (model.Prop2 != prop2) setAndTagChanged(() => model.Prop2 = prop2);

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

Альтернативный способ заключается в использовании другой неиспользуемой функции, "или равного" оператора двоичного присваивания. Следующий код показывает, как:

private bool conditionalSet(bool condition, Action action)
{
    if (condition) action();
    return condition;
}

// ...

bool changed = false;
changed |= conditionalSet(model.Prop1 == prop1, () => model.Prop1 = prop1);
changed |= conditionalSet(model.Prop2 == prop2, () => model.Prop2 = prop2);
  • 0
    Мне не повезло, что 2-й компилируется? это только .net 3.5? public int eye {get; задавать; } private void testClosure () {var i = 0; bool изменено = ложно; Действие <Action> setAndTagChanged = (action) => {updated = true; действие (); }; setAndTagChanged (глаз = 1); }
  • 0
    Лямбды доступны только в C # 3.0 или выше. Это означает, что у вас должен быть .Net 3.5.
Показать ещё 2 комментария
38

Несколько других атрибутов из пространства имен System.Diagnostics весьма полезны.

DebuggerBrowsable позволит вам скрыть переменные из окна отладчика (мы используем его для всех частных резервных переменных открытых свойств). Наряду с этим DebuggerStepThrough заставляет отладчик перешагивать этот код, очень полезный для немых свойств (возможно, он должен быть преобразован в авто-свойства, если вы можете взять зависимость от компилятора С# 3.0). В качестве примера

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string nickName;
public string NickName    {
    [DebuggerStepThrough]
    get { return nickName; }
    [DebuggerStepThrough]
    set { this.nickName = value; }
}
  • 6
    Осторожно! DebuggerStepThrough очень удобен, но его следует использовать только в тривиальных реализациях. Когда вы отлаживаете, методы, отмеченные этим атрибутом, полностью пропускаются отладчиком, как будто их там нет (что скрывает от вас детали реализации, так как вы пройдете один шаг сразу за ним). Точки останова внутри метода никогда не сработают.
  • 11
    Хм ... изящный трюк, но я думаю, что я взял бы поля, которые были бы видны в отладчике из-за того, что вся эта куча в моем классе
Показать ещё 2 комментария
37

Я бы сказал, что использование некоторых системных классов для методов расширения очень удобно, например System.Enum, вы можете сделать что-то вроде ниже...

[Flags]
public enum ErrorTypes : int {
    None = 0,
    MissingPassword = 1,
    MissingUsername = 2,
    PasswordIncorrect = 4
}

public static class EnumExtensions {

    public static T Append<T> (this System.Enum type, T value) where T : struct
    {
        return (T)(ValueType)(((int)(ValueType) type | (int)(ValueType) value));
    }

    public static T Remove<T> (this System.Enum type, T value) where T : struct
    {
        return (T)(ValueType)(((int)(ValueType)type & ~(int)(ValueType)value));
    }

    public static bool Has<T> (this System.Enum type, T value) where T : struct
    {
        return (((int)(ValueType)type & (int)(ValueType)value) == (int)(ValueType)value);
    }

}

...

//used like the following...

ErrorTypes error = ErrorTypes.None;
error = error.Append(ErrorTypes.MissingUsername);
error = error.Append(ErrorTypes.MissingPassword);
error = error.Remove(ErrorTypes.MissingUsername);

//then you can check using other methods
if (error.Has(ErrorTypes.MissingUsername)) {
    ...
}

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

  • 1
    Ницца; но я бы использовал «Включить» вместо «Добавить», поскольку «Добавление» подразумевает порядок, которого значения могут не иметь.
  • 0
    @devstuff - это хороший момент, я даже не думал об этом с этой точки зрения.
Показать ещё 2 комментария
37

Возможность иметь типы перечисления имеет значения, отличные от int (по умолчанию)

public enum MyEnum : long
{
    Val1 = 1,
    Val2 = 2
}

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

MyEnum e = (MyEnum)123;
  • 0
    Но ценности должны быть сдержанными. Так что не плавает или удваивается и т. Д. Просто ради полноты;)
  • 3
    Почему вы хотите иметь возможность присваивать перечислению только старые значения? Не является ли смысл перечисления ограничивать выбор значений?
Показать ещё 9 комментариев
36

RealProxy позволяет создавать собственные прокси-серверы для существующих типов.

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

В принципе класс .NET RealProxy позволяет создавать так называемый прозрачный прокси для другого типа. Прозрачный в этом случае означает, что он выглядит полностью как прокси-целевой объект для своего клиента - но это действительно не так: это экземпляр вашего класса, который получен из RealProxy.

Это позволяет применять мощные и всесторонние услуги перехвата и "посредничества" между клиентом и любыми методами или свойствами, вызываемыми на реальном целевом объекте. Соедините эту мощь с шаблоном factory (IoC и т.д.), И вы можете передавать прозрачные прокси вместо реальных объектов, позволяя вам перехватывать все вызовы реальным объектам и выполнять действия до и после каждого вызова метода. На самом деле, я считаю, что это самая функциональность .NET для удаленного доступа к доменам приложений, процессов и машин:.NET перехватывает весь доступ, отправляет сериализованную информацию удаленному объекту, получает ответ и возвращает его вашему коду.

Может быть, пример даст понять, как это может быть полезно: я создал стоп ссылочной службы для моего последнего задания в качестве архитектора предприятия, который специфицировал стандартную внутреннюю композицию ( "стек" ) любых новых служб WCF в подразделении. Модель утверждала, что уровень доступа к данным для (скажем) службы Foo service IDAL<Foo>: создает Foo, читает Foo, обновляет Foo, удаляет Foo. Сервисные разработчики использовали предоставленный общий код (от меня), который мог бы найти и загрузить требуемый DAL для службы:

IDAL<T> GetDAL<T>(); // retrieve data access layer for entity T

Стратегии доступа к данным в этой компании часто были, но, с точки зрения производительности. Как архитектор, я не мог следить за каждым разработчиком сервиса, чтобы убедиться, что он написал уровень доступа к данным. Но то, что я мог сделать в шаблоне GetDAL factory, - это создать прозрачный прокси для запрошенного DAL (после того, как общий код модели службы, расположенный в DLL и загруженный), и использовать высокопроизводительные API-интерфейсы времени для профиля всех вызовов для любого метода DAL. Тогда ранжирование отставания - это просто вопрос сортировки таймингов DAL по убыванию общего времени. Преимущество этого для профилирования разработки (например, в среде IDE) заключается в том, что это можно сделать и в производственной среде, чтобы обеспечить SLA.

Вот пример тестового кода, который я написал для "профайлера сущностей", который был распространенным кодом для создания профилирующего прокси для любого типа с одной строкой:

[Test, Category("ProfileEntity")]
public void MyTest()
{
    // this is the object that we want profiled.
    // we would normally pass this around and call
    // methods on this instance.
    DALToBeProfiled dal = new DALToBeProfiled();

    // To profile, instead we obtain our proxy
    // and pass it around instead.
    DALToBeProfiled dalProxy = (DALToBeProfiled)EntityProfiler.Instance(dal);

    // or...
    DALToBeProfiled dalProxy2 = EntityProfiler<DALToBeProfiled>.Instance(dal);

    // Now use proxy wherever we would have used the original...
    // All methods' timings are automatically recorded
    // with a high-resolution timer
    DoStuffToThisObject(dalProxy);

    // Output profiling results
    ProfileManager.Instance.ToConsole();
}

Опять же, это позволяет перехватывать все методы и свойства, вызываемые клиентом на целевом объекте! В вашем производном от RealProxy классе вы должны переопределить Invoke:

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
[SecurityPermission(SecurityAction.LinkDemand, 
    Flags = SecurityPermissionFlag.Infrastructure)] // per FxCop
public override IMessage Invoke(IMessage msg)
{
    IMethodCallMessage msgMethodCall = msg as IMethodCallMessage;
    Debug.Assert(msgMethodCall != null); // should not be null - research Invoke if this trips. KWB 2009.05.28

    // The MethodCallMessageWrapper
    // provides read/write access to the method 
    // call arguments. 
    MethodCallMessageWrapper mc =
        new MethodCallMessageWrapper(msgMethodCall);

    // This is the reflected method base of the called method. 
    MethodInfo mi = (MethodInfo)mc.MethodBase;

    IMessage retval = null;

    // Pass the call to the method and get our return value
    string profileName = ProfileClassName + "." + mi.Name;

    using (ProfileManager.Start(profileName))
    {
        IMessage myReturnMessage =
           RemotingServices.ExecuteMessage(_target, msgMethodCall);

        retval = myReturnMessage;
    }

    return retval;
}

Разве это не увлекательно, что может сделать .NET? Единственное ограничение состоит в том, что целевой тип должен быть получен из MarshalByRefObject. Надеюсь, это кому-то поможет.

  • 0
    Ранее я использовал RealProxy для создания подробных журналов разговоров о взаимодействии COM ... Очень удобно, когда вы хотите дать поставщику представление о том, где именно происходит сбой его компонента! Я только хотел бы, чтобы можно было использовать это на объектах любого типа ...
  • 0
    Да, я согласен и не совсем уверен, почему существует ограничение MarshalByRefObject ...
Показать ещё 3 комментария
35

Произвольные вложенные области {}


1. Для более точного определения области видимости

{ внутри элементов }, {, используя только фигурные скобки }, { > без инструкции управления }.

void MyWritingMethod() {

    int sameAge = 35;


    { // scope some work
        string name = "Joe";
        Log.Write(name + sameAge.ToString());
    }


    { // scope some other work
        string name = "Susan";
        Log.Write(name + sameAge.ToString());
    }

    // I'll never mix up Joe and Susan again
}

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


2. Для улучшения кода или визуальной семантики

Например, этот код записи XML следует за уровнем отступов фактического генерируемого XML (то есть Visual Studio будет отступать от фигурных скобок)

XmlWriter xw = new XmlWriter(..);

//<root>
xw.WriteStartElement("root");
{
    //<game>
    xw.WriteStartElement("game");
    {
        //<score>#</score>
        for (int i = 0; i < scores.Length; ++i) // multiple scores
            xw.WriteElementString("score", scores[i].ToString());

    }
    //</game>
    xw.WriteEndElement();
}
//</root>
xw.WriteEndElement();

3. Mimic a 'with' statement

(Еще одно использование, чтобы сохранить временную работу из основной области)
Предоставлено Patrik: иногда используется для имитации VB-с-выражением на С#.

var somePerson = this.GetPerson();  // whatever 
{ 
    var p = somePerson; 
    p.FirstName = "John"; 
    p.LastName = "Doe"; 
    //... 
    p.City = "Gotham"; 
} 

Для проницательного программиста.

  • 0
    Это то, что я иногда использовал, чтобы имитировать VB "с-оператором" в C #. var somePerson = this.GetPerson (); // что угодно {var p = somePerson; p.FirstName = "Джон"; p.LastName = "Доу"; // ... p.City = "Gotham"; }
  • 1
    +1, я делал это раньше. Однако это только сбило с толку моих коллег. Пример с писателем XML довольно приятный, хотя. Я бы сделал счет. ForEach (...), хотя, чтобы сделать отступ правильно.
Показать ещё 3 комментария
35

Я только что узнал об этом сегодня - и я работаю с С# в течение 5 лет!

Это идентификатор псевдонима пространства имен:

extern alias YourAliasHere;

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

34

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

        int? x = null;
        int y;
        if (x.HasValue)
            y = x.Value;
  • 2
    Как можно использовать обнуляемый тип без использования HasValue?
  • 4
    Как это: INT? Икс; если (х! = ноль)
Показать ещё 8 комментариев
33

Моим любимым является

global::

ключевое слово, чтобы избежать пространства имен с некоторыми нашими сторонними поставщиками кода...

Пример:

global::System.Collections.Generic.List<global::System.String> myList =
    new global::System.Collections.Generic.List<global::System.String>();
  • 1
    Он работает так же, как спецификатор доступа, но в отношении пространств имен, то есть global когда используется с квалификатором псевдонима пространства имен :: ссылается на глобальное пространство имен, которое является пространством имен по умолчанию для любой программы на C #. Пример использования здесь - msdn.microsoft.com/en-us/library/c3ay4x3d.aspx
32

определение типов

Кто-то опубликовал, что они пропускают typedefs, но вы можете сделать это, как это

using ListOfDictionary = System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, string>>;

и объявите его как

ListOfDictionary list = new ListOfDictionary();
  • 10
    Просто имейте в виду, что этот метод в лучшем случае подходит для текущего файла. Вам нужно будет добавить это в начало каждого файла в вашем проекте.
  • 0
    Это не скрытая особенность. Это полностью задокументировано в спецификации.
Показать ещё 3 комментария
31

Я прочитал все семь страниц, и я пропустил их:

string.join

Я видел много циклов for-loops для преобразования списка элементов в строку с разделителями. Это всегда боль, чтобы убедиться, что вы не начинаете с разделителя и не заканчиваете разделителем. Встроенный метод упрощает:

String.Join(",", new String[] { "a", "b", "c"});

TODO в комментарии

На самом деле это не функция С#, а больше возможностей Visual Studio. Когда вы начинаете свой комментарий с TODO, он добавляется в список задач Visual Studio (View → Список задач. Комментарии)

// TODO: Implement this!
throw new NotImplementedException();

Методы расширения соответствуют Generics

Вы можете комбинировать методы расширения с Generics, когда вы думаете о подсказке ранее в этом разделе, вы можете добавлять расширения к определенным интерфейсам

public static void Process<T>(this T item) where T:ITest,ITest2 {}

Enumerable.Range

Просто хотите список целых чисел?

Enumerable.Range(0, 15)

Я попытаюсь придумать еще...

  • 8
    это действительно совет VS, но помимо TODO мы также используем: QUESTION, HACK, BUG, FIX, REFACTOR, RESOURCE: (с URL-адресом, откуда вы получили совет / код). Вы можете добавить столько, сколько захотите, с помощью Tools > Параметры> Список задач И с таким КИ, как Хадсон, который это поднимает, это здорово!
  • 0
    Фактически, вы можете добавить метод расширения ко всему с помощью универсальных методов расширения ...
Показать ещё 3 комментария
30

Ширина в string.Format()

Console.WriteLine("Product: {0,-7} Price: {1,5}", product1, price1);
Console.WriteLine("Product: {0,-7} Price: {1,5}", product2, price2);

производит

Изображение 951

from Блог Prabir | Скрытая функция С#

30

Вы можете "использовать" несколько объектов в одном операторе using.

using (Font f1= new Font("Arial", 10.0f), f2 = new Font("Arial", 10.0f))
{
    // Use f1 and f2.
}

Обратите внимание, что уже есть ответ, в котором говорится, что вы можете сделать это:

using (Font f1= new Font("Arial", 10.0f))
using (Font f2 = new Font("Arial", 10.0f))
{    }

Что отличает меня.

  • 1
    Вы, вероятно, не можете использовать свой метод, если f2 зависит от f1, верно? Я все время использую второй метод, когда вы «используете» NHibernate ISession, а затем используете его для создания транзакции NHibernate ITransaction, которая также является одноразовой.
  • 3
    Обратите внимание, что вы можете указывать несколько объектов в одном и том же операторе using, если они одного типа.
Показать ещё 1 комментарий
29

Мне нравится ключевое слово continue.

Если вы нажмете условие в цикле и не хотите ничего делать, но продвигайте цикл, просто вставьте "продолжить".

например:.

foreach(object o in ACollection)
{
  if(NotInterested)
     continue;
}
  • 3
    -1: использование продолжения, перерыва и т. Д. В одном шаге от гото (которые являются злыми). Они усложняют процесс выполнения в сложных программах и в конечном итоге приведут вас к написанию непонятного кода.
  • 57
    +1, чтобы компенсировать Джон Кейдж. Если продолжить / прервать зло, то и возвращение. continue / break может использоваться для преждевременного завершения цикла (continue завершает только текущую итерацию, break завершает весь цикл), так же как return может использоваться для преждевременного завершения функции. И рано может быть намного лучше, чем глубоко вложенные, если. И гото не зло, просто не часто нужно. Он получил плохую репутацию из «кода спагетти», часто создаваемого на старых языках, в котором отсутствуют лучшие конструкции. Наличие этих лучших конструкций приводит к более чистому коду и гораздо меньшей потребности в goto, но не в отсутствии. Используйте правильный инструмент для работы.
Показать ещё 5 комментариев
28

Инициализация поля по требованию в одной строке:

public StringBuilder Builder
{
    get { return _builder ?? (_builder = new StringBuilder()); }
}

Я не уверен, что я чувствую, что С# поддерживает выражения присваивания, но эй, это там: -)

  • 0
    Я разочарован, так много лет кодирую в .NET без использования этого синтаксиса ... Отлично!
  • 0
    К сожалению, эта функция дублирована. Видел то же самое выше.
Показать ещё 5 комментариев
28

Полный доступ к стеку вызовов:

public static void Main()
{
  StackTrace stackTrace = new StackTrace();           // get call stack
  StackFrame[] stackFrames = stackTrace.GetFrames();  // get method calls (frames)

  // write call stack method names
  foreach (StackFrame stackFrame in stackFrames)
  {
    Console.WriteLine(stackFrame.GetMethod().Name);   // write method name
  }
}

Итак, если вы возьмете первый, вы знаете, в какой функции вы находитесь. Если вы создаете вспомогательную функцию трассировки - возьмите ее до последней, вы узнаете своего вызывающего.

  • 2
    Одна вещь, которая может сбить вас с толку, это если вы находитесь в режиме отладки или выпуска. Трассировка стека может отличаться из-за оптимизации. Это облажало меня в неудачной попытке отрегулировать вызывающих абонентов определенных методов: moffdub.wordpress.com/2008/07/01/method-regulator-pattern
  • 2
    Вы можете использовать <System.Runtime.CompilerServices.MethodImpl (Runtime.CompilerServices.MethodImplOptions.NoInlining)>, чтобы помочь с этой проблемой.
Показать ещё 5 комментариев
28

@lomaxx Я также узнал, что на днях (в то же время я узнал ваш совет), что теперь вы можете иметь разрозненные уровни доступа по одному и тому же свойству:

public string Name { get; private set;}

Таким образом, только сам класс может установить свойство Name.

public MyClass(string name) { Name = name; }
  • 0
    Этот синтаксис был добавлен в C # в 2.0. C ++ позволил это на .Net 1.0 и 1.1
  • 0
    К сожалению, это единственная применимая комбинация различных прав доступа 'get; внутренний набор; ' не будет работать
Показать ещё 3 комментария
27

JavaScript-подобные анонимные встроенные функции

Возврат строки:

var s = new Func<String>(() =>
{
    return "Hello World!";
})();

Возвращает более сложный объект:

var d = new Func<Dictionary<Int32, String>>(() =>
{
    return new Dictionary<Int32, String>
    {
        { 0, "Foo" },
        { 1, "Bar" },
        { 2, "..." }
    };
})();

Практический пример:

var tr = new TableRow();

tr.Cells.AddRange
(
    new[]
    {
        new TableCell { Text = "" },
        new TableCell { Text = "" },
        new TableCell { Text = "" },

        new TableCell
        {
            Text = new Func<String>(() =>
            {
                return @"Result of a chunk of logic, without having to define
                         the logic outside of the TableCell constructor";
            })()
        },

        new TableCell { Text = "" },
        new TableCell { Text = "" }
    }
);

Примечание. Вы не можете повторно использовать имена переменных внутри области встроенной функции.


Альтернативный синтаксис

// The one-liner
Func<Int32, Int32, String> Add = (a, b) => Convert.ToString(a + b);

// Multiple lines
Func<Int32, Int32, String> Add = (a, b) =>
{
    var i = a + b;

    return i.ToString();
};

// Without parameters
Func<String> Foo = () => "";

// Without parameters, multiple lines
Func<String> Foo = () =>
{
    return "";
};

Сократите строку и добавьте горизонтальный эллипс...

Func<String, String> Shorten = s => s.Length > 100 ? s.Substring(0, 100) + "&hellip;" : s;
27

Программисты, перемещающиеся с C/С++, могут пропустить это:

В С#% (оператор модуля) работает над поплавками!

  • 0
    Это все еще связано с неточностями логики с плавающей точкой. Если вы действительно хотите точный модуль при использовании десятичного числа, вы должны использовать десятичное число.
27

Свойство Environment.UserInteractive.

Отчеты свойств UserInteractive false для процесса Windows или службы, такой как IIS, которая работает без пользовательский интерфейс. Если это свойство false, не отображать модальные диалоги или сообщений, потому что нет графический пользовательский интерфейс для пользователя для взаимодействия с.

  • 0
    +1 хорошо для уменьшения "прогресса обновления" замедлений
  • 0
    Определенно звучит полезно для создания служб или утилит командной строки.
Показать ещё 1 комментарий
27

Вложение с помощью выражений

Обычно мы делаем это следующим образом:

StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter()) {
    using (IndentedTextWriter itw = new IndentedTextWriter(sw)) {
        ... 
    }
}

Но мы можем сделать это следующим образом:

StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter())
using (IndentedTextWriter itw = new IndentedTextWriter(sw)) {
    ... 
}
  • 8
    Эта особенность относится к ключевому слову using? Это просто выглядит как типичный синтаксис, где оператор (например, if, using, while) работает либо со следующим оператором, либо с блоком операторов. Опускать фигурные скобки в этих ситуациях не рекомендуется в руководствах по стилю кода, которые я читал.
  • 2
    Это не специфично для использования, вы можете написать: if (Something) using (new Pen ()) using (new Brush ()) for (;;) DoSometing ();
Показать ещё 5 комментариев
27

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

Даже если методы расширения не являются такими секретными (LINQ основан на них), это может быть не так очевидно, насколько полезными и читаемыми они могут быть для вспомогательных методов:

//for adding multiple elements to a collection that doesn't have AddRange
//e.g., collection.Add(item1, item2, itemN);
static void Add<T>(this ICollection<T> coll, params T[] items)
 { foreach (var item in items) coll.Add(item);
 }

//like string.Format() but with custom string representation of arguments
//e.g., "{0} {1} {2}".Format<Custom>(c=>c.Name,"string",new object(),new Custom())
//      result: "string {System.Object} Custom1Name"
static string Format<T>(this string format, Func<T,object> select, params object[] args)
 { for(int i=0; i < args.Length; ++i)
    { var x = args[i] as T;
      if (x != null) args[i] = select(x);
    }
   return string.Format(format, args);
 }
27

Два моих личных фаворита, которые я вижу редко:

  • Фрагменты (особенно для свойств, которые были улучшены для Visual Studio 2008)
  • ObsoleteAttribute
  • 2
    Мне очень нравится фрагмент переключателя. Делает включение enum sooo намного проще;)
  • 0
    я регулярно использую фрагмент для свойств, вызывающих OnPropertyChanged в сеттере. очень кстати.
Показать ещё 5 комментариев
26

Легко определить тип, с которым была объявлена ​​ переменная (из мой ответ):

using System;
using System.Collections.Generic;

static class Program
{
    public static Type GetDeclaredType<T>(T x)
    {
        return typeof(T);
    }

    // Demonstrate how GetDeclaredType works
    static void Main(string[] args)
    {
        IList<string> iList = new List<string>();
        List<string> list = null;

        Console.WriteLine(GetDeclaredType(iList).Name);
        Console.WriteLine(GetDeclaredType(list).Name);
    }
}

Результаты:

IList`1
List`1

И его имя (заимствовано из "Получить имя переменной" ):

static void Main(string[] args)
{
    Console.WriteLine("Name is '{0}'", GetName(new {args}));
    Console.ReadLine();
}

static string GetName<T>(T item) where T : class
{
    var properties = typeof(T).GetProperties();
    return properties[0].Name;
}

Результат: Name is 'args'

  • 2
    На самом деле, неплохо. Первый взгляд на образец вводит в заблуждение. Я запомню этот трюк. :)
  • 0
    Не можете просто написать Console.WriteLine(iList.GetType().Name); ?
Показать ещё 3 комментария
26

С#?? нулевой коалесцирующий оператор -

Не очень скрытый, но редко используемый. Наверное, потому что многие разработчики пробегают милю, когда видят условное? оператора, поэтому они запускают два, когда видят это. Используется:

string mystring = foo ?? "foo was null"

а не

string mystring;
if (foo==null)
    mystring = "foo was null";
else
    mystring = foo;
  • 0
    Я понимаю вашу точку зрения, но второй пример можно было бы сократить, не используя ?? оператор. Прямо как строка mystring = foo == null? "foo was null": foo; Признаюсь, еще долго, но, по крайней мере, немного короче.
  • 3
    Если условие является функцией, которая вычисляет результат, с помощью троичного оператора вы в конечном итоге вызовете функцию дважды (в случае, если она оценивается как true). В то время как использование ?? позвоню только один раз.
Показать ещё 1 комментарий
26

На самом деле это не скрытая функция С#, но я недавно обнаружил класс WeakReference и был потрясен им (хотя это может быть предвзято благодаря тому, что это помогло мне найти решение конкретной проблемы моего...)

  • 0
    +1 - вам может быть интересно узнать, что он, похоже, используется в MVVMLight Toolkit в части «Передача сообщений».
  • 0
    ссылка на juanformoso.com.ar/post/2008/07/30/Weak-References.aspx не работает.
25

AppDomain.UnhandledException Event также является кандидатом на скрытие.

Это событие предоставляет уведомление о неперехваченных исключениях. Он позволяет приложению регистрировать информацию об исключении, прежде чем системный обработчик по умолчанию сообщает об исключении пользователю и завершает работу приложения. Если имеется достаточная информация о состоянии приложения, могут быть предприняты другие действия - например, сохранение данных программы для последующего восстановления. Предупреждение рекомендуется, так как данные программы могут быть повреждены, если исключения не обрабатываются.

Мы видим, что даже на этом сайте многие люди задаются вопросом, почему их приложение не запускается, почему оно разбилось и т.д. Событие AppDomain.UnhandledException может быть очень полезно для таких случаев, поскольку оно обеспечивает возможность по крайней мере для регистрации причины сбоя приложения.

25

В С# есть некоторые действительно скрытые ключевые слова и функции, связанные с недокументированным классом TypedReference. Следующие недопустимые ключевые слова:

  • __makeref
  • __reftype
  • __refvalue
  • __arglist

Примеры использования:

// Create a typed reference
int i = 1;
TypedReference tr1 = __makeref(i);
// Get the type of a typed reference
Type t = __reftype(tr1);
// Get the value of a typed referece
int j = __refvalue(tr1, int); 
// Create a method that accepts and arbitrary number of typed references
void SomeMethod(__arglist) { ...
// Call the method
int x = 1;
string y = "Foo";
Object o = new Object();
SomeMethod(__arglist(x,y,o));
// And finally iterate over method parameters
void SomeMethod(__arglist) {
    ArgIterator ai = new ArgIterator(__arglist);
while(ai.GetRemainingCount() >0)
{
      TypedReference tr = ai.GetNextArg();
      Console.WriteLine(TypedReference.ToObject(tr));
}}
  • 0
    Почему вы хотите использовать эти скрытые недокументированные ключевые слова. Они, вероятно, скрыты и недокументированы по причине, означающей, что они могут измениться в любое время. На мой взгляд, это рискованно.
  • 3
    Да, рискованно использовать их, эти скрытые ключевые слова, которые были введены перед обобщениями, чтобы ускорить взаимодействие, P / Invoke, потому что эти функции позволяют избегать блокирования / распаковки типов значений.
Показать ещё 3 комментария
25

true и false Операторы действительно странные.

Более полный пример можно найти здесь.

Изменить: есть связанный вопрос SO Что такое фальшивый оператор в С# для?

  • 2
    Работаю с C # в течение 5 лет и никогда не видел, чтобы настоящий оператор был перегружен. Имеет смысл, так как вы можете перегрузить арифметические и операторы равенства. Спасибо!
  • 0
    хорошо, никогда не думал об этом. Может быть полезным в некоторых случаях ...
Показать ещё 4 комментария
25

Директива предпроцессора #if DEBUG. Это полезно для тестирования и отладки (хотя я обычно предпочитаю единичный тестовый маршрут).

string customerName = null;
#if DEBUG
  customerName = "Bob"
#endif

Он выполнит только код, если для Visual Studio установлено значение скомпилируйте в режиме "Отладка". В противном случае блок кода будет игнорируется компилятором (и неактивен в Visual Studio).

  • 3
    Обратите внимание, что вы можете определить любой символ, а затем использовать условную компиляцию для этого символа. DEBUG просто автоматически определяется для вас по умолчанию.
  • 11
    [Условный («DEBUG»)] - помеченные методы обычно делают код чище и проще для чтения.
Показать ещё 1 комментарий
24

Я не нашел никого, кто использует string.Join, чтобы присоединиться к строкам с помощью разделителя. Каждый пишет о том же уродливом for-loop

var sb = new StringBuilder();
var count = list.Count();
for(int i = 0; i < count; i++)
{
  if (sb.Length > 0) sb.Append(seperator);
  sb.Append(list[i]);
}

return sb.ToString();

вместо

return string.Join(separator, list.ToArray());
  • 1
    Вы забыли, if (sb.Length > 0) sb.Append(seperator); удалить предыдущий разделитель. Вы также хотите кэшировать любые функции Count() для сохранения переоценок и string.Join() только для массивов. Как и многие другие разработчики, у меня есть свои собственные методы расширения, которые чище, что string.Join()
  • 0
    Я исправил ошибки. Моя точка зрения заключается в том, что я вижу первый фрагмент кода больше, чем потом
Показать ещё 1 комментарий
23

Я обнаружил, что об этой функции знают лишь немногие разработчики.

Если вам нужен метод, который работает с переменной value-type через некоторый интерфейс (реализованный этим типом значений), легко избежать бокса во время вызова метода.

Пример кода:

using System;
using System.Collections;

interface IFoo {
    void Foo();
}
struct MyStructure : IFoo {
    public void Foo() {
    }
}
public static class Program {
    static void MethodDoesNotBoxArguments<T>(T t) where T : IFoo {
        t.Foo();
    }
    static void Main(string[] args) {
        MyStructure s = new MyStructure();
        MethodThatDoesNotBoxArguments(s);
    }
}

Код IL не содержит никаких инструкций в поле:

.method private hidebysig static void  MethodDoesNotBoxArguments<(IFoo) T>(!!T t) cil managed
{
  // Code size       14 (0xe)
  .maxstack  8
  IL_0000:  ldarga.s   t
  IL_0002:  constrained. !!T
  IL_0008:  callvirt   instance void IFoo::Foo()
  IL_000d:  ret
} // end of method Program::MethodDoesNotBoxArguments

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       15 (0xf)
  .maxstack  1
  .locals init ([0] valuetype MyStructure s)
  IL_0000:  ldloca.s   s
  IL_0002:  initobj    MyStructure
  IL_0008:  ldloc.0
  IL_0009:  call       void Program::MethodDoesNotBoxArguments<valuetype MyStructure>(!!0)
  IL_000e:  ret
} // end of method Program::Main

См. Richter, J. CLR через С#, 2-е издание, глава 14: Интерфейсы, раздел Об общих и интерфейсных ограничениях.

См. также мой ответ на другой вопрос.

  • 0
    и почему бы просто не поставить «Где T: структура», чтобы избежать бокса ...
  • 3
    @AWC: задача состоит в том, чтобы передать экземпляр некоторого интерфейса в наш метод. Так что мы можем вызывать методы этого интерфейса на переданном экземпляре. Объявления типа void BoxingMethod(IFoo x) вызывают бокс, если x является типом значения. Ваш пример не позволяет вызывать методы интерфейса. Код выше позволяет такие звонки без бокса.
23

Частичные методы

Чарли Калверт объясняет частичные методы в своем блоге

У Скотта Кейта есть приятная демонстрация частичного метода здесь

  • Точки расширяемости в Code Generated class (LINQ to SQL, EF)
  • Не компилируется в dll, если он не реализован (проверьте его с помощью .NET Reflector)
  • 0
    Я не знаю, частичные методы всегда казались мне запахом кода ...
  • 0
    Во многих случаях возможно. Хорошо для отделения сгенерированного кода от кода разработчика.
Показать ещё 3 комментария
22

На основании того, что этот поток должен иметь право "вещи, о которых вы не знали о С# до недавнего времени, несмотря на то, что вы уже все знали", моя личная функция - асинхронные делегаты.

До тех пор, пока я не прочитаю книгу Джеффа Рихтера С#/CLR (отличная книга, все, кто должен ее читать .NET) Я не знал, что вы можете назвать любого делегата, используя BeginInvoke/EndInvoke. Я обычно делаю много вызовов ThreadPool.QueueUserWorkItem (что, как мне кажется, очень похоже на то, что делает делегат BeginInvoke), но добавление стандартизованного шаблона join/rendezvous иногда может быть действительно полезным.

  • 0
    Да BeginInvoke / EnInvoke использует пул потоков, я узнал об этом на этом форуме. См stackoverflow.com/questions/442991/... и msdn.microsoft.com/en-us/library/2e08f6yc.aspx .
  • 1
    «вещи, которые вы не знали о C # до недавнего времени, несмотря на то, что думали, что вы уже все знаете» +1
21

ОК, это может показаться очевидным, но я хочу упомянуть метод Object.Equals (статический, с двумя аргументами).

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

if ((x == y) || ((x != null && y != null) && x.Equals(y)))
{
    ...
}

Когда вы можете просто написать:

if (Object.Equals(x, y))
{
    ...
}

(Object.Equals фактически реализуется точно так же, как в первом примере кода)

21

Как насчет IObservable?

Практически все знают IEnumerable, но их математическое двойное кажется неизвестным IObservable. Может быть, потому, что его новый в .NET 4.

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

Вместе с расширения Rx он изменит, как мы имеем дело с событиями. Чтобы проиллюстрировать, насколько мощным является проверка очень короткого примера здесь.

21

Цели атаки

Каждый видел его. В основном, когда вы видите это:

[assembly: ComVisible(false)]

Часть "assembly:" этого атрибута является целевой. В этом случае атрибут применяется к сборке, но есть и другие:

[return: SomeAttr]
int Method3() { return 0; } 

В этом примере атрибут применяется к возвращаемому значению.

21

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

bool myFlag  = System.Xml.XmlConvert.ToBoolean(myAttribute.Value);

Примечание: поскольку логический тип в xml принимает 1 и 0 в дополнение к "true" и "false" в качестве допустимых значений, использование сравнения строк в этом случае подвержено ошибкам.

21

Несколько человек упомянули использование блоков, но я думаю, что они намного полезнее, чем люди поняли. Подумайте о них как об инструменте AOP бедного человека. У меня есть множество простых объектов, которые фиксируют состояние в конструкторе, а затем восстанавливают его в методе Dispose(). Это позволяет мне обернуть часть функциональности в используемом блоке и убедиться, что состояние будет восстановлено в конце. Например:

using(new CursorState(this, BusyCursor));
{
    // Do stuff
}

CursorState фиксирует текущий курсор, используемый формой, затем устанавливает форму для использования курсора. В конце он восстанавливает исходный курсор. Я делаю такие вещи, например, захватывая выбор и текущую строку на сетке перед обновлением и т.д.

  • 1
    Я использовал этот трюк один раз, и он оказался бесполезным, потому что Win32 делает что-то, чтобы восстановить курсор для вас, по крайней мере, в WinForms
  • 0
    Я только что объявил класс с именем DisposableAction, который выполняет действие в конструкторе и выполняет его в распоряжении. Действительно удобно. build.lokad.com/doc/shared/html/E5642EA0.htm
Показать ещё 4 комментария
21

Я опаздываю на эту вечеринку, поэтому мои первые выборы уже приняты. Но я не видел, чтобы кто-нибудь упоминал об этом камне:

Параллельные расширения .NET Framework

У него есть такие вещи, как замена с помощью Parallel.For или foreach с Parallel.ForEach


Параллельный пример:
Как вы думаете, сколько объектов CLR может быть создано за одну секунду? Изображение 952
См. приведенный ниже пример:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace ObjectInitSpeedTest
{
   class Program
   {
       //Note: don't forget to build it in Release mode.
       static void Main()
       {
           normalSpeedTest();           
           parallelSpeedTest();

           Console.ForegroundColor = ConsoleColor.White;
           Console.WriteLine("Press a key ...");
           Console.ReadKey();
       }

       private static void parallelSpeedTest()
       {
           Console.ForegroundColor = ConsoleColor.Yellow;
           Console.WriteLine("parallelSpeedTest");

           long totalObjectsCreated = 0;
           long totalElapsedTime = 0;

           var tasks = new List<Task>();
           var processorCount = Environment.ProcessorCount;

           Console.WriteLine("Running on {0} cores", processorCount);

           for (var t = 0; t < processorCount; t++)
           {
               tasks.Add(Task.Factory.StartNew(
               () =>
               {
                   const int reps = 1000000000;
                   var sp = Stopwatch.StartNew();
                   for (var j = 0; j < reps; ++j)
                   {
                       new object();
                   }
                   sp.Stop();

                   Interlocked.Add(ref totalObjectsCreated, reps);
                   Interlocked.Add(ref totalElapsedTime, sp.ElapsedMilliseconds);
               }
               ));
           }

           // let complete all the tasks
           Task.WaitAll(tasks.ToArray());

           Console.WriteLine("Created {0:N} objects in 1 sec\n", (totalObjectsCreated / (totalElapsedTime / processorCount)) * 1000);
       }

       private static void normalSpeedTest()
       {
           Console.ForegroundColor = ConsoleColor.Green;
           Console.WriteLine("normalSpeedTest");

           const int reps = 1000000000;
           var sp = Stopwatch.StartNew();
           sp.Start();
           for (var j = 0; j < reps; ++j)
           {
               new object();
           }
           sp.Stop();

           Console.WriteLine("Created {0:N} objects in 1 sec\n", (reps / sp.ElapsedMilliseconds) * 1000);
       }
   }
}
  • 0
    Однако это не совсем языковая функция, поскольку это просто библиотека, которая умело использует другие языковые функции.
21

Рядом со всеми крутыми были упомянуты. Не уверен, хорошо ли это известно или нет.

Инициализация конструктора свойств/объекта С#:

var foo = new Rectangle() 
{ 
    Fill = new SolidColorBrush(c), 
    Width = 20, 
    Height = 20 
};

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

Я заметил что-то смешное - у вас может быть запятая в конце списка свойств, без синтаксической ошибки. Так что это также верно:

var foo = new Rectangle() 
{ 
    Fill = new SolidColorBrush(c), 
    Width = 20, 
    Height = 20,
};
  • 8
    Запятая в конце делает игру со значениями намного проще :)
  • 0
    Завершающая запятая также полезна для сгенерированного кода. Вы заметите, что это верно для многих ситуаций. Я сталкиваюсь с этим чаще всего, когда делаю перечисление. :)
Показать ещё 2 комментария
20

Еще одна заметка о обработчиках событий: вы можете просто создать метод расширения с расширением:

public static class EventExtensions {
    public static void Raise<T>(this EventHandler<T> @event, 
                                object sender, T args) where T : EventArgs {
        if(@event!= null) {
            @event(sender, args);
        }
    }
}

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

public class MyImportantThing {
    public event EventHandler<MyImportantEventEventArgs> SomethingHappens;
    ...
    public void Bleh() {
        SomethingHappens.Raise(this, new MyImportantEventEventArgs { X=true });
    }
}

Этот метод обладает дополнительным преимуществом в обеспечении соблюдения стандарта кодирования (используя EventHandler<>).

Нет смысла писать одну и ту же точную функцию снова и снова. Возможно, следующая версия С#, наконец, будет иметь InlineAttribute, который может быть помещен в метод расширения, и заставит компилятор встроить определение метода (что сделает этот способ почти стандартным и самым быстрым).

edit: удалена временная переменная внутри метода расширения на основе комментариев

  • 0
    Вам не нужна переменная temp . Он используется для предотвращения асинхронного изменения, но для этого достаточно параметра event .
  • 0
    Я не уверен в этом. Параметр является ссылочным типом, поэтому @event - это тот же объект, что и событие в классе (если только не происходит что-то с синтаксисом события, о котором я не знаю). Поскольку код присваивания и проверки является рекомендуемой практикой, я ожидаю, что здесь определен оператор присваивания, чтобы обойти эту проблему. Несмотря на это, это не мешает быть в безопасности.
Показать ещё 1 комментарий
20

Извинения за публикацию настолько поздно, что я новичок в Stack Overflow, так что упустил предыдущую возможность.

Я нахожу, что EventHandler<T> - отличная особенность недоработанной структуры.

Большинство разработчиков С#, с которыми я сталкиваюсь, все еще определяют делегат обработчика настраиваемых событий, когда они определяют пользовательские события, которые просто больше не нужны.

Вместо:

public delegate void MyCustomEventHandler(object sender, MyCustomEventArgs e);

public class MyCustomEventClass 
{
    public event MyCustomEventHandler MyCustomEvent;
}

вы можете пойти:

public class MyCustomEventClass 
{
    public event EventHandler<MyCustomEventArgs> MyCustomEvent;
}

который намного более краткий, плюс вы не попадаете в вопрос о том, следует ли делегировать делегат в файле .cs для класса, содержащего событие, или производного класса EventArgs.

  • 0
    Вот ссылка на фрагмент, который облегчает создание пользовательских классов EventArgs и событие, которое его использует: stackoverflow.com/questions/1157072/…
19
string.Empty

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

И это довольно хорошо скрыто, пока кто-то не говорит вам об этом.

  • 0
    Разве "" не короче, чем строка. Пусто?
  • 2
    @Arnis: string.Empty более «правильный». Для справки .Empty не совсем редко используемое значение, большинство типов значений реализуют его, и рекомендации Microsoft поощряют его использование (и действительно, статические версии общих значений, MinValue и MaxValue - два других общих значения). Это может предотвратить ненужное создание дублированных типов значений, когда может использоваться один и тот же экземпляр (типы неизменяемых значений).
Показать ещё 11 комментариев
19

Не совсем скрыто, но полезно. Когда у вас есть enum с flags, вы можете использовать shift-left, чтобы сделать вещи более ясными. например.

[Flags]
public enum ErrorTypes {
    None              = 0,
    MissingPassword   = 1 << 0,
    MissingUsername   = 1 << 1,
    PasswordIncorrect = 1 << 2 
}
  • 2
    Вы можете это объяснить? Не уверен, что я следую, как это работает или что это делает
  • 2
    Это просто смещение битов в двоичном представлении числа, 0001 << 1 становится 0010, 0001 << 2 становится 0100, 0001 << 3 становится 1000
Показать ещё 9 комментариев
19

С# 3.0. Понимание запросов LINQ - это полномасштабные монадические осмысления a la Haskell (на самом деле они были разработаны одним из дизайнеров Haskell). Они будут работать для любого родового типа, следующего за "шаблоном LINQ", и позволяют писать в виде чистого монадического функционального стиля, что означает, что все ваши переменные неизменяемы (как будто единственными переменными, которые вы использовали, были IDisposable и IEnumerable в операциях использования и foreach). Это полезно для того, чтобы поддерживать объявления переменных близко к тому, где они используются, и убедиться, что все побочные эффекты явно объявлены, если они есть вообще.

 interface IFoo<T>
  { T Bar {get;}
  }

 class MyFoo<T> : IFoo<T> 
  { public MyFoo(T t) {Bar = t;}
    public T Bar {get; private set;} 
  }

 static class Foo 
  { public static IFoo<T> ToFoo<T>(this T t) {return new MyFoo<T>(t);}

    public static void Do<T>(this T t, Action<T> a) { a(t);}

    public static IFoo<U> Select<T,U>(this IFoo<T> foo, Func<T,U> f) 
     { return f(foo.Bar).ToFoo();
     }
  }

 /* ... */

 using (var file = File.OpenRead("objc.h"))
 { var x = from f in file.ToFoo()
           let s = new Scanner(f)
           let p = new Parser {scanner = s}
           select p.Parse();

   x.Do(p => 
    { /* drop into imperative code to handle file 
         in Foo monad if necessary */      
    });

 }
19

Я вижу, что многие люди реплицируют функциональность Nullable<T>.GetValueOrDefault(T).

  • 0
    По моему мнению, ?? в большинстве случаев чище, по крайней мере, для примитивных типов (0 вместо a. GetValueOrDefault ()). Дополнительное бремя предоставления фактического значения по умолчанию исчезает, когда вы действительно хотите другое значение.
  • 0
    @OregonGhost: GetValueOrDefault (с предоставленным значением по умолчанию или без него) примерно на 33% быстрее, чем ??, хотя на самом деле вы не заметите разницу, если не выполните миллионы итераций.
Показать ещё 2 комментария
18

Мой любимый атрибут: InternalsVisibleTo

На уровне сборки вы можете объявить, что другая сборка может видеть ваши внутренности. Для целей тестирования это совершенно замечательно.

Вставьте это в свой AssemblyInfo.cs или его эквивалент, и ваша тестовая сборка получит полный доступ ко всем внутренним материалам, требующим тестирования.

[assembly: InternalsVisibleTo("MyLibrary.Test, PublicKey=0024...5c042cb")]

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

Доступно в .Net Framework 2.0+, Compact Framework 2.0+ и XNA Framework 1.0 +.

  • 3
    Да, отлично подходит для тестовых сборок ... И только для тестовых сборок. Правильно? ;)
  • 0
    @JohannesH - Да, я бы согласился. Есть веская причина для того, чтобы иметь внутреннее ключевое слово, и это держать вещи внутренними :)
17

Вам нужно вернуть пустой IEnumerable?

public IEnumerable<T> GetEnumerator(){
  yield break;
}
  • 9
    Уже в BCL System.Linq.Enumerable.Empty<T>()
  • 2
    @chak Обертка return new T[0]; , Есть много способов сделать это. Это всего лишь один. Но это интересный ...
Показать ещё 3 комментария
17

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

{
    string test2 = "3";
    Console.Write(test2);
}

Console.Write(test2); //compile error

test2 живет только в скобках.

  • 2
    Это верно и для C ++.
  • 0
    Правда какого-либо языка с лексическим ограничением нет?
Показать ещё 1 комментарий
17

ключевое слово extern для ссылки на две версии сборок с одинаковыми именами полного типа.

17

Мне нравится использовать символ @для SQL-запросов. Он сохраняет sql хорошо и отформатирован и без необходимости окружать каждую строку разделителем строк.

string sql = @"SELECT firstname, lastname, email
               FROM users
               WHERE username = @username AND password = @password";
  • 0
    не могу согласиться больше
  • 0
    Я использую '@' все время, но я никогда не думал о том, чтобы охватить несколько строк! :-П
Показать ещё 5 комментариев
16

Вы можете хранить цвета в Enum.

public enum MyEnumColors : uint
{
    Normal          = 0xFF9F9F9F,
    Active          = 0xFF8EA98A,
    Error           = 0xFFFF0000
}
  • 3
    Да, но для чего? Почему бы вместо этого не создать строго типизированные атрибуты статического класса? С таким же успехом можно использовать строки для хранения произвольных больших двоичных данных вместо использования классов, как это часто делалось в BASIC.
  • 4
    Я думаю, что FFire пытается привлечь внимание к тому факту, что вы можете указать базовый тип (в данном случае uint) для перечисления
16

Возможность использования выражений LINQ для выполнения сильно типизированного отражения:

static void Main(string[] args)
{
  var domain = "matrix";
  Check(() => domain);
  Console.ReadLine();
}

static void Check<T>(Expression<Func<T>> expr)
{
  var body = ((MemberExpression)expr.Body);
  Console.WriteLine("Name is: {0}", body.Member.Name);
  Console.WriteLine("Value is: {0}", ((FieldInfo)body.Member)
   .GetValue(((ConstantExpression)body.Expression).Value));
}

// output:
// Name is: 'domain'
// Value is: 'matrix'

Более подробную информацию можно найти на Как узнать переменную или имя параметра в С#?

  • 0
    +1 Вы также можете сделать это с анонимными типами.
15

исправлено выражение

Это утверждение запрещает сборщику мусора перемещать подвижную переменную. Исправлено также можно использовать для создания буферов фиксированного размера.

Фиксированный оператор устанавливает указатель на управляемую переменную и "выдает" эту переменную во время выполнения инструкции.

stackalloc

В стеке stackalloc выделяется блок памяти.

  • 4
    Я думаю, что это должно включать пример того, почему / как это может быть полезно
  • 1
    @Neil Вы можете перейти по ссылкам в ответе. Там вы можете увидеть несколько примеров и подробностей в разделе замечаний. Скажите, если это не будет удовлетворительным, я обновлю ответ с примером кода.
Показать ещё 4 комментария
15

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

Цитата из блога BCL Team:

Ограниченные области выполнения (CER) существуют, чтобы помочь разработчику написать ей чтобы поддерживать согласованность. CLR не гарантирует, что код верен, но CLR поднять все вызванные работой среды (например, асинхронные исключения) либо до запуска кода, либо после его завершения. В сочетании с ограничения на то, что разработчик может положить в ССВ, это полезный способ предоставления сильных гарантий относительно будет ли выполняться ваш код. ЦЭИ жадно подготовлены, что означает, что когда мы увидим один, мы с нетерпением ждем JIT любой код, найденный в его статически обнаруживаемый граф вызовов. Если хост CLR заботится о стеке переполнения, мы будем исследовать некоторую сумму пространства стека (хотя, возможно, недостаточно места для стека для любого произвольный метод *). Мы также задерживаем нить прерывается до тех пор, пока CER не будет законченный ход.

Это может быть полезно при внесении изменений в несколько полей структуры данных атомным способом. Таким образом, это помогает иметь транзакции по объектам.

Также CriticalFinalizerObject кажется скрытым (по крайней мере, кто не пишет небезопасный код). CriticalFinalizerObject гарантирует, что сбор мусора выполнит финализатор. При распределении финализатор и его график вызовов готовятся заранее.

  • 0
    Хм ... может ли это сделать прерывание безопасности безопасным, если они были написаны для него?
15

Использование оператора ~ ~ с атрибутом FlagAttribute и перечислением
Когда-нибудь мы будем использовать атрибут Flag с перечислением для выполнения побитовой манипуляции в перечислении.

 [Flags]
 public enum Colors
 {
    None  = 0,
    Red   = 1,
    Blue  = 2,
    White = 4,
    Black = 8,
    Green = 16,
    All   = 31 //It seems pretty bad...
 }

Обратите внимание, что значение опции "Все", которое в перечислении довольно странно.
Вместо этого мы можем использовать оператор ~ ~ с флаговым перечислением.

 [Flags]
 public enum Colors
 {
    None  = 0,
    Red   = 1,
    Blue  = 2,
    White = 4,
    Black = 8,
    Green = 16,
    All   = ~0 //much better now. that mean 0xffffffff in default.
 }
  • 0
    должно быть 32, я думаю, что не 31, что вы говорите?
  • 3
    Помните, что это помеченное перечисление. Таким образом, в этом случае «All» равно (Red & Blue & White & Black & Green), тогда оно также будет равно (00001 & 00010 & 00100 & 01000 & 10000) и равно 11111 и равно 31. Число 32 должно быть 100000, но 11111.
Показать ещё 1 комментарий
15

Вы можете добавлять и удалять делегаты с меньшим количеством ввода.

Обычный способ:

handler += new EventHandler(func);

Меньше ввод текста:

handler += func;
  • 4
    Видел, что все время ... просто заставляю мою руку чесаться :) Кроме того, если вы набираете + = в VS, а затем [TAB] [TAB] для генерации обработчика событий, тип делегата все еще вставляется ... это немного раздражает.
  • 0
    @ sr pt: Нет, это не функциональность. Это функция по умолчанию в Visual studio (я даже считаю, что она существует с 2005 года). Visual Studio имеет больше этих автозаполненных фрагментов.
Показать ещё 1 комментарий
15

Вы можете использовать любой символ Юникода в именах С#, например:

public class MyClass
{
    public string Hårføner()
    {
        return "Yes, it works!";
    }
}

Вы даже можете использовать escape-последовательности Unicode. Это эквивалентно приведенному выше:

public class MyClass
{
    public string H\u00e5rføner()
    {
        return "Yes, it (still) works!";
    }
}
  • 0
    Для любого, кто находится за пределами англоязычного мира, это гигантский скачок в сторону не зависящих от языка вычислений. Для нас (для меня, по крайней мере), это на самом деле несколько странно и старомодно - придерживаться 26 букв английского алфавита. Вы знаете, что-то вроде имен файлов 8.3.
  • 4
    Хм, да, давайте смешаем английские идентификаторы BCL и ключевые слова с неанглийскими идентификаторами. Теперь люди из других стран больше не могут использовать ваш код без Intellisense: P;) Нет. Это означает, что можно использовать любой символ, но в этом нет никакой реальной выгоды.
Показать ещё 2 комментария
15

Как насчет FlagsAttribute в перечислении? Он позволяет выполнять побитовые операции... взял меня навсегда, чтобы узнать, как делать побитовые операции в .NET красиво.

  • 5
    Я думаю, что вы можете делать побитовые операции с любым перечислением, флаги влияют только на метод ToString ()
  • 10
    Это на самом деле довольно опасно - это не влияет на назначенные значения - это просто атрибут маркера, указывающий ваше намерение, поэтому, если вы явно не объявите свои значения перечисления как степени двух, вы будете думать, что у вас есть поразрядное перечисление, но вы не будете на самом деле ...
Показать ещё 1 комментарий
15

Мне часто приходится сталкиваться с необходимостью использования универсального параметра-объекта в представлении в базовом классе.

public abstract class BaseListControl<ListType,KeyType,ParameterType>
                 : UserControl 
                 where ListType : BaseListType
                 && ParameterType : BaseParameterType, new
{

    private const string viewStateFilterKey = "FilterKey";

    protected ParameterType Filters
    {
        get
        {
            if (ViewState[viewStateFilterKey] == null)
                ViewState[viewStateFilterKey]= new ParameterType();

            return ViewState[viewStateFilterKey] as ParameterType;
        }
        set
        {
            ViewState[viewStateFilterKey] = value;
        }
    }

}

Использование:

private void SomeEventHappened(object sender, EventArgs e)
{
    Filters.SomeValue = SomeControl.SelectedValue;
}

private void TimeToFetchSomeData()
{
    GridView.DataSource = Repository.GetList(Filters);
}

Этот маленький трюк с "где ParameterType: BaseParameterType, new" - это то, что заставляет его действительно работать.

С помощью этого свойства в моем базовом классе я могу автоматизировать обработку пейджинга, установку значений фильтра для фильтрации gridview, упрощения сортировки и т.д.

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

  • 0
    Может быть, я ненормальный, но я не уверен, что понимаю код. Не могли бы вы опубликовать более полный пример?
  • 1
    Это довольно просто на самом деле. Если у вас есть GridView с парой DropDownLists для фильтрации содержимого, вы просто помещаете значения в объект фильтра, который сохраняется между обратными передачами, и отправляете его в качестве параметра в метод, который выбирает данные из БД. Вы просто реализуете свой UserControl, наследующий от BaseListControl, а база заботится о сохранении «состояния» между постбеками.
Показать ещё 2 комментария
14

Вы можете поместить несколько атрибутов в одну пару квадратных скобок:

    [OperationContract, ServiceKnownType(typeof(Prism)), ServiceKnownType(typeof(Cuboid))]
    Shape GetShape();
  • 1
    Я ненавижу, когда CodeAnalysis помещает его атрибут SuppressMessage в другие атрибуты ... Для меня я люблю выравнивать все атрибуты.
14

Когда класс реализует INotifyPropertyChanged, и вы хотите сообщить системе привязки (WPF, Silverlight и т.д.), что несколько связанных свойств объекта (ViewModel) изменились, вы можете поднять событие PropertyChanged с помощью null или String.Empty.

Это задокументировано в MSDN, но примеры кода и статьи часто не объясняют эту возможность. Я нашел это очень полезным.

public class BoundObject : INotifyPropertyChanged {

    private int _value;
    private string _text;

    public event PropertyChangedEventHandler PropertyChanged;

    public int Value {
        get {
            return _value;
        }
        set {
            if (_value != value) {
                _value = value;
                OnPropertyChanged("Value");
            }
        }
    }

    public string Text {
        get {
            return _text;
        }
        set {
            if (_text != value) {
                _text = value;
                OnPropertyChanged("Text");
            }
        }
    }

    public void Init(){
        _text = "InitialValue";
        _value = 1;
        OnPropertyChanged(string.Empty);
    }

    public void Reset() {
        _text = "DefaultValue";
        _value = 0;
        OnPropertyChanged(string.Empty);
    }

    private void OnPropertyChanged(string propertyName) {
        PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);

        if (PropertyChanged != null) {
            PropertyChanged(this, e);
        }
    }
}
14

При определении пользовательских атрибутов вы можете использовать их с [MyAttAttribute] или с помощью [MyAtt]. Когда классы существуют для обеих записей, возникает ошибка компиляции.

Специальный символ @может использоваться для различения между ними:

[AttributeUsage(AttributeTargets.All)]
public class X: Attribute
{}

[AttributeUsage(AttributeTargets.All)]
public class XAttribute: Attribute
{}

[X]      // Error: ambiguity
class Class1 {}

[XAttribute]   // Refers to XAttribute
class Class2 {}

[@X]      // Refers to X
class Class3 {}

[@XAttribute]   // Refers to XAttribute
class Class4 {}
  • 0
    Конечно, лучшее решение - исправить одно или оба имени, чтобы они не конфликтовали друг с другом. Но это полезно знать в случае работы со сторонними библиотеками, которые сделали это неправильно, и что вы не можете это исправить.
14

ConditionalAttribute

Позволяет сообщить компилятору опустить вызов метода, помеченного атрибутом при определенных условиях (#define).

Тот факт, что вызов метода опущен, также означает, что его параметры не оцениваются. Это очень удобно, и это позволяет вам вызывать дорогостоящие функции проверки в Debug.Assert() и не беспокоиться о том, что они замедляют вашу сборку релизов.

13

упрощенное ORM-сопоставление с использованием LINQ

Рассмотрим эту таблицу:

[MessageId] INT,
[MessageText] NVARCHAR(MAX)
[MessageDate] DATETIME

... И эта структура:

struct Message
{
    Int32 Id;
    String Text;
    DateTime Date;
}




Вместо того, чтобы делать что-то по строкам:

List<Message> messages = new List<Message>();

foreach (row in DataTable.Rows)
{
    var message = new Message
    {
        Id = Convert.ToInt32(row["MessageId"]),
        Text = Convert.ToString(row["MessageText"]),
        Date = Convert.ToDateTime(row["MessageDate"])
    };

    messages.Add(message);
}

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

var messages = DataTable.AsEnumerable().Select(r => new Message
{
    Id = Convert.ToInt32(r["MessageId"]),
    Text = Convert.ToString(r["MessageText"]),
    Date = Convert.ToDateTime(r["MessageDate"])
}).ToList();

Этот подход может быть вложенным, как и в цикле.

  • 2
    Это интересный подход, но я не уверен, что это проще для глаз. На мой взгляд, для обычного программиста это определенно не легче.
  • 10
    +1 за значительное улучшение читабельности. Я думаю, что новый код намного проще для глаз. Хотя сегодня среднестатистический программист C # все еще программирует процедурно, это быстро изменится. Я обнаружил, что когда вы показываете большинству процедурных программистов версию LINQ, они сразу понимают и говорят что-то вроде «вау!» и хочу знать, как они могут использовать это сами.
Показать ещё 3 комментария
13

Тип-вывод для методов factory

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

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

public class Tuple<V1, V2> : Tuple
{
    public readonly V1 v1;
    public readonly V2 v2;

    public Tuple(V1 v1, V2 v2)
    {
      this.v1 = v1;
      this.v2 = v2;
    }
}

Всем известно, как создать экземпляр, например:

Tuple<int, string> tup = new Tuple<int, string>(1, "Hello, World!");

Не совсем ракетостроение, теперь мы можем, конечно, изменить объявление типа переменной на var, например:

var tup = new Tuple<int, string>(1, "Hello, World!");

Все еще хорошо известно, чтобы немного отвлечь здесь статический метод с параметрами типа, с которым все должны быть знакомы:

public static void Create<T1, T2>()
{
    // stuff
}

Вызов, это снова общее знание, сделано следующим образом:

Create<float, double>();

То, что большинство людей не знает, состоит в том, что если аргументы общего метода содержат все типы, которые он требует, они могут быть выведены, например:

public static void Create<T1, T2>(T1 a, T2 b)
{
    // stuff
}

Эти два вызова идентичны:

Create<float, string>(1.0f, "test");
Create(1.0f, "test");

Так как T1 и T2 выводятся из аргументов, которые вы передали. Объединяя эти знания с ключевым словом var, мы можем добавить второй статический класс со статическим методом, например:

public abstract class Tuple
{
    public static Tuple<V1, V2> Create<V1, V2>(V1 v1, V2 v2)
    {
        return new Tuple<V1, V2>(v1, v2);
    }
}

Достичь этого эффекта:

var tup = Tuple.Create(1, "Hello, World!");

Это означает, что типы переменной: "tup", параметры типа "Создать" и возвращаемое значение "Создать" - все они выводятся из типов, которые вы передаете в качестве аргументов для Create

Полный код выглядит примерно так:

public abstract class Tuple
{
    public static Tuple<V1, V2> Create<V1, V2>(V1 v1, V2 v2)
    {
        return new Tuple<V1, V2>(v1, v2);
    }
}

public class Tuple<V1, V2> : Tuple
{
    public readonly V1 v1;
    public readonly V2 v2;

    public Tuple(V1 v1, V2 v2)
    {
        this.v1 = v1;
        this.v2 = v2;
    }
}

// Example usage:
var tup = Tuple.Create(1, "test");

Это дает вам полное описание методов factory во всех случаях!

13

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

public static class Strings
{
    public static bool IsNullOrEmpty(this string value)
    {
        return string.IsNullOrEmpty(value);
    }
}

Это позволит вам немного сократить код.

var input = Console.ReadLine();
if (input.IsNullOrEmpty())
{
    Console.WriteLine("try again");
}
  • 1
    Я надеюсь, вы понимаете, что этот пример невозможен, потому что IsNullOrEmpty уже является членом строки типа. Методы расширения не могут иметь того же имени, что и любой член (статический или нестатический) того типа, который он расширяет.
  • 3
    Я использую это в 2 проектах для работы прямо сейчас плюс многочисленные личные проекты. Я поместил метод IsNullOrEmpty в класс Strings вместе с другими моими методами расширения для строкового класса. У меня не было проблем с компилятором, сообщающим, что имя метода недопустимо, и я использую его в своем коде уже 3 или 4 месяца.
Показать ещё 6 комментариев
13

FlagsAttribute, небольшая, но приятная функция при использовании enum для создания битмакса:

[Flags]
public enum ConfigOptions
{
    None    = 0,
    A       = 1 << 0,
    B       = 1 << 1,
    Both    = A | B
}

Console.WriteLine( ConfigOptions.A.ToString() );
Console.WriteLine( ConfigOptions.Both.ToString() );
// Will print:
// A
// A, B
  • 1
    +1 за явную демонстрацию поведения ToString () для комбинированных флагов
13

Как насчет этого:

#if DEBUG
            Console.Write("Debugging");
#else
            Console.Write("Final");
#endif

Когда ваше решение скомпилировано с определением DEBUG, оно выведет "Отладка".

Если ваш компилятор настроен на Release, он напишет "Final".

13

Я не стал по-настоящему ценить "использование" блоков до недавнего времени. Они делают вещи намного более аккуратными:)

13

Лямбда-выражения

Func<int, int, int> add = (a, b) => (a + b);

Неясные строковые форматы

Console.WriteLine("{0:D10}", 2); // 0000000002

Dictionary<string, string> dict = new Dictionary<string, string> { 
    {"David", "C#"}, 
    {"Johann", "Perl"}, 
    {"Morgan", "Python"}
};

Console.WriteLine( "{0,10} {1, 10}", "Programmer", "Language" );

Console.WriteLine( "-".PadRight( 21, '-' ) );

foreach (string key in dict.Keys)
{
    Console.WriteLine( "{0, 10} {1, 10}", key, dict[key] );             
}
12

Пара, о которой я могу думать:

[field: NonSerialized()]
public EventHandler event SomeEvent;

Это предотвращает сериализацию события. "Поле:" указывает, что атрибут должен быть применен к полю поддержки событий.

Еще одна известная функция - переопределение обработчиков событий add/remove:

public event EventHandler SomeEvent
{
    add
    {
        // ...
    }

    remove
    {
        // ...
    }
}
12

Вы можете использовать generics для проверки (время компиляции), если аргумент метода реализует два интерфейса:

interface IPropA 
{
    string PropA { get; set; } 
}

interface IPropB 
{
    string PropB { get; set; }
}

class TestClass 
{
    void DoSomething<T>(T t) where T : IPropA, IPropB 
    {
        MessageBox.Show(t.PropA);
        MessageBox.Show(t.PropB);
    }
}

То же самое с аргументом, который унаследован от базового класса и интерфейса.

  • 0
    В этом случае вам нужно будет сделать var a = (IPropA) t; var propa = a.PropA; На всякий случай интерфейс явно реализован?
  • 0
    @ Марк, я не понимаю твой вопрос, поэтому не могу на него ответить.
Показать ещё 2 комментария
12

Что-то я пропустил в течение долгого времени: вы можете сравнивать строки с помощью

"string".equals("String", StringComparison.InvariantCultureIgnoreCase)

вместо выполнения:

"string".ToLower() == "String".ToLower();
  • 2
    Я знаю, что это должен быть правильный подход, но меня раздражает многословие самого перечисления.
  • 7
    На самом деле, StringComparison.OrdinalIgnoreCase следует использовать во многих случаях.
Показать ещё 2 комментария
12

Падение через switch - case может быть достигнуто путем отсутствия кода в case (см. case 0) или с использованием специального goto case (см. case 1) или goto default (см. case 2):

switch (/*...*/) {
    case 0: // shares the exact same code as case 1
    case 1:
        // do something
        goto case 2;
    case 2:
        // do something else
        goto default;
    default:
        // do something entirely different
        break;
}
  • 1
    Обратите внимание, что «переход к делу» не рекомендуется в этой статье: msdn.microsoft.com/en-us/vcsharp/aa336815.aspx
  • 1
    Я был бы один , чтобы предположить , что любое использование «GOTO» не рекомендуется вообще - но это вовсе не означает , что это не полезно.
Показать ещё 5 комментариев
11

Настройте методы расширения на null; это не приведет к выбросу NullReferenceException.

Пример приложения: вы можете определить альтернативу для ToString() под названием ToStringOrEmpty(), которая вернет пустую строку при вызове null.

  • 0
    Я был довольно увлечен, когда обнаружил этот факт, так как он позволяет мне в некоторых случаях писать более чистый код в моем текущем проекте.
  • 0
    Что еще лучше - ToStringOrDefault (этот объект obj, объект по умолчанию) - и ToStringOrEmpty (этот объект obj) {obj.ToStringOrDefault (String.Empty); }
11

Синтаксис делегата эволюционировал по последовательным версиям С#, но мне все еще сложно их запомнить. К счастью, делегаты Action<> и Func<> легко запоминаются.

Например:

  • Action<int> - это метод делегата, который принимает один аргумент int и возвращает void.
  • Func<int> - это метод делегата, который не принимает аргументов и возвращает int.
  • Func<int, bool> - это метод делегата, который принимает один аргумент int и возвращает bool.

Эти функции были представлены в версии 3.5.NET framework.

  • 0
    Действие <T> доступно в .NET 2.0. Обидно, что Func нет, потому что это облегчит использование лямбд.
  • 0
    Некоторые из них: Действие, Сравнение, Конвертер, EventHandler, Func, Predicate (см. Stackoverflow.com/questions/319789/… )
11

Существуют операторы для выполнения implicit и explicit пользовательское преобразование типов между объявленным классом и одним или несколькими произвольными классами. Оператор implicit эффективно позволяет моделировать перегрузку оператора присваивания, что возможно в таких языках, как С++, но не С#.

Кажется, что это не очень часто встречающаяся функция, но она фактически используется в LINQ to XML ( System.Xml.Linq), где вы можете неявно преобразовывать строки в объекты XName. Пример:

XName tagName = "x:Name";

Я обнаружил эту функцию в этой статье о том, как имитировать множественное наследование в С#.

11
  • Я еще не могу прокомментировать, но обратите внимание, что по умолчанию Visual Studio 2008 автоматически переходит к свойствам, поэтому в этом случае атрибут DebuggerStepThrough больше не нужен.

  • Кроме того, я не заметил, чтобы кто-нибудь показывал, как объявить lambda без параметров (полезно для реализации Action < > )

    () => DoSomething(x);

Вы должны также прочитать о закрытии - я недостаточно умен, чтобы правильно объяснить их. Но в основном это означает, что компилятор делает умные вещи так, что x в этой строке кода будет работать, даже если он выходит из области видимости после создания лямбда.

  1. Недавно я также обнаружил, что вы можете притворяться, что игнорируете параметр лямбда:

    (e, _) => DoSomething(e)

Это не игнорирует его, просто _ является допустимым идентификатором. Таким образом, вы не можете игнорировать оба параметра, как это, но я думаю, что это своего рода аккуратный способ указать, что мы не заботимся об этом параметре (обычно EventArgs, который является .Empty).

11

Мне нравится, что я могу использовать LINQ для объектов на простом .NET 2.0 (т.е. без необходимости устанавливать .NET 3.5 везде). Все, что вам нужно, это реализация всех методов расширения запроса. См. LINQBridge

10

Вы можете изменить схему округления, используя:

var value = -0.5;
var value2 = 0.5;
var value3 = 1.4;

Console.WriteLine( Math.Round(value, MidpointRounding.AwayFromZero) ); //out: -1
Console.WriteLine(Math.Round(value2, MidpointRounding.AwayFromZero)); //out: 1
Console.WriteLine(Math.Round(value3, MidpointRounding.ToEven)); //out: 1
10

Вложенные классы могут обращаться к закрытым членам внешнего класса.

public class Outer
{
    private int Value { get; set; }

    public class Inner
    {
        protected void ModifyOuterMember(Outer outer, int value)
        {
            outer.Value = value;
        }
    }
}

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

public class Cheater : Outer.Inner
{
    protected void MakeValue5(Outer outer)
    {
        ModifyOuterMember(outer, 5);
    }
}

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

10

Оператор присваивания Or довольно хорош. Вы можете написать это:

x |= y

вместо этого:

x = x | y

Это часто бывает практичным, если вам нужно переменная или свойство (x в примере), которое начинается с false, но вы хотите изменить его на значение какой-либо другой логической переменной/свойство только тогда, когда это другое значение true.

  • 0
    msgstr "начинается с false, но вы хотите изменить его на значение какой-либо другой логической переменной / свойства, только когда это другое значение равно true." это означает "x = y" ^^ РЕДАКТИРОВАТЬ: не заметил огромную некромантию, которую я только что выполнил. Ну что ж.
10

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

[ThreadStatic]
private static int _ThreadStaticInteger;

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

И еще одна вещь для потоков приложений ASP.NET используется повторно, поэтому, если вы измените значение, которое может быть использовано для другого запроса страницы.

Тем не менее, я нашел это полезным несколько раз. Например, при создании настраиваемого класса транзакций, который:

using (DbTransaction tran = new DbTransaction())
{
    DoQuery("...");
    DoQuery("...");    
}

Конструктор DbTransaction задает поле ThreadStatic самому себе и сбрасывает его в null в методе dispose. DoQuery проверяет статическое поле и if!= Null использует текущую транзакцию, если не по умолчанию используется другое. Нам не нужно передавать транзакцию каждому методу плюс он упрощает перенос других методов, которые не были оригинальны, которые должны использоваться с транзакцией внутри транзакции...

Только одно использование:)

10

Одна из вещей, о которых многие не знают, - это некоторые из директив препроцессора С#. Вы можете использовать #error This is an error. для генерации ошибки компилятора и #warning This is a warning.

Я обычно использую их, когда я разрабатываю подход сверху вниз как список "todo". Я буду #error Implement this function или #warning Eventually implement this corner case в качестве напоминания.

  • 0
    Это хорошо, но я недавно обнаружил, что #warning в Silverlight не останавливает сборку, когда задано предупреждение как ошибки, хотя это и должно быть.
  • 2
    Разве вы не можете просто использовать TODO: комментарии в Visual Studio? dotnetperls.com/todo-comments-visual-studio
Показать ещё 2 комментария
10

TrueForAll-метод List<T>:

List<int> s = new List<int> { 6, 1, 2 };

bool a = s.TrueForAll(p => p > 0);
  • 0
    Linq: s.All (p => p> 0);
10

Чтобы вызвать конструктор базового класса, просто поместите base() в строку с конструктором.
Чтобы вызвать метод базового класса, вы можете просто поместить base.MethodName() внутри метода производного класса

class ClassA 
{
  public ClassA(int a)
  {
    //Do something
  }

  public void Method1()
  {
     //Do Something
  }
}

class ClassB : ClassA
{
  public ClassB(int a) : base(a) // calling the base class constructor
  {
    //Do something
  }

  public void Method2()
  {
    base.Method1();               // calling the base class method
  }
}

Конечно, вы можете вызвать методы базового класса, просто сказав base.MethodName()

9

Чтобы проверить, является ли IEnumerable <T> пустым с LINQ, используйте:

IEnumerable <T>.Any();

  • Сначала я использовал (IEnumerable <T>.Count()!= 0)...
    • Что лишнее приводит к перечислению всех элементов в IEnumerable <T>.
  • В качестве улучшения этого я продолжал использовать (IEnumerable <T>.FirstOrDefault() == null)...
    • Что лучше...
  • Но IEnumerable <T>.Any() является самым кратким и работает наилучшим образом.
  • 0
    Почему enumerable.FirstOrDefault () == null является улучшением?
  • 1
    Потому что это потенциально не вызывает перечисление всей коллекции.
Показать ещё 3 комментария
9

Мне очень нравятся неявные общие параметры для функций. Например, если у вас есть:

public void DoStuff<T>(T value);

Вместо того, чтобы называть его следующим образом:

DoStuff<int>(5);

Вы можете:

DoStuff(5);

И он будет определять общий тип из типа параметра.

  • Это не работает, если вы вызываете метод через отражение.
  • Я помню некоторые странные проблемы в Mono.
  • 0
    Но это работает только с одним параметром типа. Как только вы добавите два, это заставит вас явно указывать типы.
9

Работа с перечислениями.

Преобразование строки в Enum:

enum MyEnum
{
    FirstValue,
    SecondValue,
    ThirdValue
}

string enumValueString = "FirstValue";
MyEnum val = (MyEnum)Enum.Parse(typeof(MyEnum), enumValueString, true)
  • Я использую это, чтобы загрузить значение CacheItemPriority в моих приложениях ASP.NET из таблицы настроек в базе данных, чтобы я мог контролировать кеширование (наряду с другими настройками) динамически, не откладывая приложение.

При сравнении переменных типа enum вам не нужно указывать int:

MyEnum val = MyEnum.SecondValue;
if (val < MyEnum.ThirdValue)
{
    // Do something
}
9

IEnumerable SelectMany, который выравнивает список списков в один список. Скажем, у меня есть список Orders, и каждый Order имеет список LineItems в этом порядке.

Я хочу узнать, сколько всего продано LineItems...

int totalItems = Orders.Select(o => o.LineItems).SelectMany(i => i).Sum();
  • 11
    int totalItems = Orders.SelectMany (o => o.LineItems) .Sum ();
9

Интерпретация строк. Это тот, который я еще не видел в этой дискуссии. Это немного неясно, но в определенных условиях это может быть полезно.

CLR хранит таблицу ссылок на литеральные строки (и программно интернированные строки). Если вы используете одну и ту же строку в нескольких местах вашего кода, она будет храниться один раз в таблице. Это может облегчить объем памяти, необходимый для выделения строк.

Вы можете проверить, является ли строка интернированной, используя String.IsInterned(string), и вы можете ставить строку, используя String.Intern(строка).

Примечание. CLR может содержать ссылку на интернированную строку после приложения или даже на конец AppDomain. Подробнее см. Документацию MSDN.

  • 0
    Стажированные строки хранятся в маленьких страницах в куче больших объектов. Если ваш набор строк не ограничен, вы будете фрагментировать LOH с небольшими, долгоживущими строковыми внутренними страницами.
9
System.Diagnostics.Debug.Assert (false);

вызовет всплывающее окно и позволит вам приложить отладчик к запущенному процессу .NET во время выполнения. Очень полезно в те моменты, когда по какой-то причине вы не можете напрямую отлаживать приложение ASP.NET.

  • 2
    Так будет и Debugger.Break ();
  • 0
    разве это не приводит к тому, что некоторые довольно неясные окна сообщений отображаются для пользователя, если одно из них происходит в процессе производства?
Показать ещё 3 комментария
9

Директивы препроцессора могут быть отличными, если вы хотите различное поведение между режимами Debug и Release.

http://msdn.microsoft.com/en-us/library/ed8yd1ha.aspx

  • 0
    Определенно. У меня есть код в проекте, над которым я работаю, который обходит экран входа в систему в режиме отладки. Делает каждый тест намного быстрее!
8

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

    public Func<int> RandomGenerator
    {
        get
        {
            var r = new Random();
            return () => { return r.Next(); };
        }
    }

    void SomeFunction()
    {
        var result1 = RandomGenerator();

        var x = RandomGenerator;
        var result2 = x();
    }
  • 0
    На самом деле это не LINQ, а лямбда-выражения.
  • 0
    И если вы продолжаете создавать Случайные объекты снова и снова, качество семян пострадает. Намного лучше (также с точки зрения пространства и времени) просто выделить статический и использовать его один раз.
Показать ещё 4 комментария
8

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

var dict = new Dictionary<int, string> { { 10, "Hello" }, { 20, "World" } };
8

С# позволяет добавлять методы настройки свойств к конкретным типам, которые реализуют свойства readonly интерфейса, даже если сама декларация интерфейса не имеет свойства. Например:

public interface IReadOnlyFoo
{
   object SomeReadOnlyProperty { get; }
}

Конкретный класс выглядит следующим образом:

internal class Foo : IReadOnlyFoo
{
   public object SomeReadOnlyProperty { get; internal set; }
}

Интересно, что класс Foo неизменен, если вы передаете его в интерфейс IReadOnlyFoo:

// Create a Foo instance
Foo foo = new Foo();

// This statement is legal
foo.SomeReadOnlyProperty = 12345;

// Make Foo read only
IReadOnlyFoo readOnlyFoo = foo;

// This statement won't compile
readOnlyFoo.SomeReadOnlyProperty = 54321;
  • 0
    Что если они вернут его Foo ?
  • 0
    Хорошая точка зрения. Я считаю полезным для большинства интерфейсов иметь свойства только для чтения. В конце концов, интерфейсы описывают поведение, и вы обычно (или, возможно, «должны только») вызывать поведение с помощью методов, а не установки свойств.
Показать ещё 2 комментария
8

Выражение для инициализации словаря в С# 3.5:

new Dictionary<string, Int64>() {{"Testing", 123}, {"Test", 125}};

8

Вместо того, чтобы делать что-то дрянное, как это:

Console.WriteLine("{0} item(s) found.", count);

Я использую следующий встроенный трюк:

Console.WriteLine("{0} item{1} found.", count, count==1 ? "" : "s");

Это будет отображаться "элемент", когда есть один элемент или "элементы", когда есть больше (или меньше), чем 1. Не так много усилий для небольшого профессионализма.

  • 1
    Это хорошо для отладки, но вы попадете в полный кошмар, когда захотите попасть в I18N и L10N
  • 0
    сладкое решение этой часто встречающейся проблемы
Показать ещё 3 комментария
8

Вы можете включить строку!

switch(name)
{
  case "Dave":
    return true;
  case "Bob":
    return false;
  default:
    throw new ApplicationException();
}

Очень удобно! и намного чище, чем куча инструкций if-else

  • 0
    Вот Это Да! Это действительно удивительно - я всегда использовал хеш-таблицу в таком случае ... Обратите внимание, что базовый код CLR здесь должен быть совершенно другим, чем в обычном переключателе.
  • 0
    Да, это превращает это в связку gotos в CLR.
8

Не конкретная вещь С#, но я - наркоман из тернарных операций.

Вместо

if (boolean Condition)
{
    //Do Function
}
else
{
    //Do something else
}

вы можете использовать сжатый

booleanCondtion ? true operation : false operation;

например.

Вместо

int value = param;
if (doubleValue)
{
    value *= 2;
}
else
{
    value *= 3;
}

вы можете ввести

int value = param * (tripleValue ? 3 : 2);

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

  • 0
    Ради правильности этот шаблон можно использовать только в таких ситуациях, как: if (условие) a = // какое-то значение else a = // другое значение
  • 0
    Это также хорошо для условных флагов - flags = (shouldRun? RUN: 0) | (следует скрыть? скрыть: 0);
Показать ещё 1 комментарий
8

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

class myClass
{
    private string myClassValue = "";

    public myClass(string myString)
    {
        myClassValue = myString;
    }

    public override string ToString()
    {
        return myClassValue;
    }

    public static myClass operator <<(myClass mc, int shiftLen)
    {
        string newString = "";
        for (int i = shiftLen; i < mc.myClassValue.Length; i++)
            newString += mc.myClassValue[i].ToString();
        mc.myClassValue = newString.ToString();
        return mc;
    }

    public static myClass operator >>(myClass mc, int shiftLen)
    {
        char[] newString = new char[shiftLen + mc.myClassValue.Length];

        for (int i = shiftLen; i < mc.myClassValue.Length; i++)
            newString[i] += mc.myClassValue[i - shiftLen];

        mc.myClassValue = new string(newString);
        return mc;
    }

    public static myClass operator +(myClass mc, string args)
    {
        if (args.Trim().Length > 1)
            mc.myClassValue += args;
        return mc;
    }

    public static myClass operator -(myClass mc, string args)
    {
        if (args.Trim().Length > 1)
        {
            Regex rgx = new Regex(args);
            mc.myClassValue = rgx.Replace(mc.myClassValue, "");
        }
        return mc;
    }
}

Я думаю, что довольно круто, чтобы иметь возможность перемещать строку влево и вправо, используя < < и → или удалить набор строк, которые следуют шаблону регулярного выражения, используя - =

myClass tmpClass = new myClass("  HelloWorld123");
tmpClass -= @"World";
tmpClass <<= 2;
Console.WriteLine(tmpClass);
  • 4
    Как скажет любой, кто работал с библиотекой C ++, в которой много перегруженных операторов, перегруженные операторы - это зло, зло, зло. Просто напишите метод, чтобы сделать это.
  • 3
    Отлично подходит для занятий по математике. Делает, например, умножение вектора и матриц очень читаемыми, просто aVector = anotherVector * aMatrix; вместо aVector = anotherVector.Multiply (aMatrix);
Показать ещё 3 комментария
7

Не уверен, что это было упомянуто или нет (11 страниц!)

Но атрибут OptionalField для классов поражает, когда вы выполняете сериализацию классов/объектов версий.

http://msdn.microsoft.com/en-us/library/ms229752(VS.80).aspx

7

Я читал книгу "Pro ASP.NET MVC Framework" (APress) и наблюдал за тем, что автор делал с объектом Dictionary, который был для меня чужим.

Он добавил новую пару "ключ/значение" без использования метода Add(). Затем он переписывал эту пару ключей/значений без необходимости проверять, существовал ли этот ключ. Например:

Dictionary<string, int> nameAgeDict = new Dictionary<string, int>();
nameAgeDict["Joe"] = 34;      // no error. will just auto-add key/value
nameAgeDict["Joe"] = 41;      // no error. key/value just get overwritten
nameAgeDict.Add("Joe", 30);   // ERROR! key already exists

Есть много случаев, когда мне не нужно проверять, есть ли у моего словаря ключ или нет, и я просто хочу добавить соответствующую пару "ключ/значение" (если необходимо, переписать существующую пару ключ/значение). к этому открытию я всегда должен был проверить, существовал ли ключ до его добавления.

7

Мне нравится

#if DEBUG
           //Code run in debugging mode

#else
           //Code run in release mode

#endif
  • 0
    Это скрытая особенность?
  • 0
    Я бы не сказал, что это скрытая функция, но во всех книгах по C #, о которых я читал, об этом не много говорят. Хорошее напоминание по крайней мере.
Показать ещё 2 комментария
7

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

7
7

Я не думаю, что кто-то упомянул о том, что добавляет? после того, как имя типа значения сделает его нулевым.

Вы можете сделать:

DateTime? date = null;

DateTime - это структура.

  • 0
    Nullable<T> не является скрытой функцией ...
  • 1
    Как и большинство из них ... Цель этого вопроса - показать вещи, которые большинство разработчиков C # могут не знать. Я определенно приветствовал бы это, так как это не то, о чем я думаю, что большинство людей знает о. (добавление? чтобы получить обнуляемый, а не обнуляемый сам)
7
[field: NonSerialized]
public event EventHandler Event;

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

Просто [NonSerialized] не работает, поскольку NonSerializedAttribute может применяться только к полям.

7
HttpContext.Current.Server.Execute 

отлично подходит для преобразования HTML в строки для обратных вызовов AJAX. Вы можете использовать это с компонентом вместо того, чтобы группировать фрагменты HTML-строки. Я смог вырезать страницу, раздутую на пару сотен КБ, практически бесполезно. Я использовал его так:

Page pageHolder = new Page();
UserControl viewControl = (UserControl)pageHolder.LoadControl(@"MyComponent.ascx");
pageHolder.Controls.Add(viewControl);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
  • 0
    +1. Хороший инструмент для модульного / интеграционного тестирования.
7

Вы можете создавать делегаты из методов расширения, как если бы они были обычными методами, выполняя параметр this. Например,

static class FunnyExtension {
    public static string Double(this string str) { return str + str; }
    public static int Double(this int num) { return num + num; }
}


Func<string> aaMaker = "a".Double;
Func<string, string> doubler = FunnyExtension.Double;

Console.WriteLine(aaMaker());       //Prints "aa"
Console.WriteLine(doubler("b"));    //Prints "bb"

Обратите внимание, что это не будет работать с методами расширения, которые расширяются тип значения; см. этот вопрос.

  • 1
    @MattDavey: Одним из применений для этого (а именно так я и обнаружил) является SomeEvent += OtherEvent.Raise , где Raise - это пользовательский метод расширения. Разница между этим и SomeEvent += OtherEvent оставлена читателю как упражнение (оно существенное). Я могу написать в блоге об этом в какой-то момент.
7

Расширенная отладка

Display

Уже упомянутые атрибуты DebuggerDisplay и DebuggerBrowsable контролируют видимость элементов и отображаемое текстовое значение. Простое переопределение ToString() приведет к тому, что отладчик будет использовать вывод этого метода.

Если вам нужен более сложный вывод, вы можете использовать/создать Debugger Visualizer, несколько примеров доступны здесь.

Сын забастовки

Microsoft предоставляет расширение отладчика, известное как SOS. Это чрезвычайно мощное (хотя и часто запутанное) расширение, которое является отличным способом диагностики "утечек", более точных нежелательных ссылок на объекты, которые больше не требуются.

Сервер Symbol для исходного кода

Следуя этим инструкциям вы сможете пройти через источник некоторых частей фреймворка.

Изменения в 2010 году

В Visual Studio 2010 несколько улучшений и новых функций:

  • Отладка параллельных задач
  • Parallel Stacks позволяют просматривать стеки вызовов нескольких потоков одновременно.
  • Историческая отладка позволяет увидеть события и нелокальные переменные во времени (пока вы включите сбор заранее). Потенциально существенное изменение в том, как вы отлаживаете вещи.
7

Свойства, отображаемые при просмотре компонентов. Свойства в режиме проектирования:

private double _Zoom = 1;

[Category("View")]
[Description("The Current Zoom Level")]
public double Zoom
{
get { return _Zoom;}
set { _Zoom = value;}
}

Делает вещи намного проще для других пользователей ваших библиотек компонентов.

  • 0
    Пока я не сделал никаких компонентов, так что это место, где я бы хотел добавить ответы в закладки, а не просто вопрос
7

Инициализация массива без указания типа элемента массива:

var pets = new[] { "Cat", "Dog", "Bird" };
  • 6
    также: string[] pets = {"Cat", "Dog", "Bird"};
  • 0
    @P Папа, спасибо, что напомнил мне, что я могу сделать это еще короче и более разборчивым
7

Встроенный (2.0) делегат MethodInvoker полезен, если вы хотите использовать встроенный код Invoke/BeginInvoke. Это позволяет избежать необходимости создания фактического делегата и отдельного метода.

    void FileMessageEvent(object sender, MessageEventArgs e)
    {

        if (this.InvokeRequired == true)
        {
            this.BeginInvoke((MethodInvoker)delegate { 
                     lblMessage.Text=e.Message; 
                     Application.DoEvents(); 
                 }
            ); 

        }
    }

Устраняет ошибку: "Невозможно преобразовать анонимный метод для ввода" System.Delegate ", потому что это не тип делегата".

7

Использование ключевого слова по умолчанию в родовом коде для возврата значения по умолчанию для типа.

public class GenericList<T>
{
    private class Node
    {
        //...

        public Node Next;
        public T Data;
    }

    private Node head;

    //...

    public T GetNext()
    {
        T temp = default(T);

        Node current = head;
        if (current != null)
        {
            temp = current.Data;
            current = current.Next;
        }
        return temp;
    }
}

Другой пример здесь

7

Здесь я недавно нашел, что было полезно:

Microsoft.VisualBasic.Logging.FileLogTraceListener

Ссылка MSDN

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

Это "скрыто", потому что оно находится в сборке Microsoft.VisualBasic... но вы также можете использовать его из С#.

  • 0
    Многие люди используют log4net или функции журнала корпоративной библиотеки. Считаете ли вы, что Microsoft.VisualBasic.Logging.FileLogTraceListener - лучший выбор?
  • 0
    Вероятно, не так полно, как другие. Но повторюсь: «Приятно то, что он является основной частью .NET и интегрирован с платформой Trace, поэтому его легко подобрать и сразу использовать».
7

Прохладный трюк для эмуляции функциональных аргументов "подстановочные знаки" (например, "_" в Haskell) при использовании lambdas:

(_, b, __) => b.DoStuff();  // only interested in b here
  • 9
    Не совсем трюк, просто выбор имен. Я думаю, это выглядит глупо, так как вы вынуждены использовать все больше подчеркиваний.
7

У вас могут быть общие методы в не-общий класс.

7

динамическое ключевое слово в С# 4.0

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

dynamic invoker=new DynamicInvoker();
dynamic result1=invoker.MyMethod1();
dynamic result2=invoker.MyMethod2();

Здесь я реализую динамический invoker.

public class DynamicInvoker : IDynamicObject
    {
        public MetaObject GetMetaObject
              (System.Linq.Expressions.Expression parameter)
        {
            return new DynamicReaderDispatch (parameter);
        }
    }

    public class DynamicDispatcher : MetaObject
    {
        public DynamicDispatcher (Expression parameter) 
                   : base(parameter, Restrictions.Empty){ }

        public override MetaObject Call(CallAction action, MetaObject[] args)
        {
            //You'll get MyMethod1 and MyMethod2 here (and what ever you call)
            Console.WriteLine("Logic to invoke Method '{0}'", action.Name);
            return this; //Return a meta object
        }
    }
7

Это не тип С#, но я только что нашел интерфейсы ISurrogateSelector и ISerializationSurrogate -

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.isurrogateselector.aspx

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.isurrogateselector.aspx

Использование этих функций в сочетании с BinaryFormatter позволяет сериализовать объекты, не связанные с сериализацией, посредством реализации суррогатного класса. Суррогатная картина хорошо понимается в информатике, особенно когда речь идет о проблеме сериализации. Я думаю, что эта реализация просто убирается как параметр конструктора в BinaryFormatter, и это слишком плохо.

Все еще - ОЧЕНЬ скрыто.:)

  • 1
    Пример того, как его использовать?
7

Явная реализация члена интерфейса, в которой реализован элемент интерфейса, но скрыт, если экземпляр не передан типу интерфейса.

7

Относительно foreach: он не использует "утиную печать", так как утиная печать ИМО относится к проверке времени выполнения. Он использует проверку структурного типа (в отличие от номинального) во время компиляции для проверки требуемого метода в типе.

  • 1
    да, утка должна быть зарезервирована для полной динамики ИМХО тоже; однако: Еогеасп действительно использует утиную типизацию в том , что она будет принуждать .NET 1.1 (нетипичные) GetEnumerator () итераторов для вас во время выполнения (это случается, как ловушка и причина Cast <> , чтобы быть очень удобно).
7

Метод Object.ReferenceEquals

Определяет, являются ли указанные экземпляры объекта одним и тем же экземпляром.

Параметры:

  • objA: System.Object - первый объект для сравнения.
  • objB: System.Object - второй объект для сравнения.

Пример:

 object o = null;
 object p = null;
 object q = new Object();

 Console.WriteLine(Object.ReferenceEquals(o, p));
 p = q;
 Console.WriteLine(Object.ReferenceEquals(p, q));
 Console.WriteLine(Object.ReferenceEquals(o, p));

Разница с "==" и ".Equals":

В принципе, тесты Equals() объекта A имеют тот же контент, что и объект B.

Метод System.Object.ReferenceEquals() всегда сравнивает ссылки. Хотя класс может обеспечить собственное поведение оператора равенства (ниже), что переопределенный оператор не вызывается, если оператор вызывается через ссылку на System.Object.

Для строк нет разницы, потому что оба == и Equals были переопределены для сравнения содержимого строки.

См. также этот ответ на другой вопрос ( "Как проверить нули в перегрузке оператора '== без бесконечной рекурсии?" ).

7

Вы вводите "prop", а затем дважды нажмите [TAB], он генерирует полезный код для ваших свойств и может ускорить ввод.

Я знаю, что это работает в VS 2005 (я использую его), но я не знаю в предыдущих версиях.

  • 5
    это особенность IDE
  • 0
    Я думаю, что это VS 2005 и выше. Я часто этим пользуюсь :-)
Показать ещё 4 комментария
7

В дополнение к ответу duncansmart, также методы расширения могут использоваться в Framework 2.0. Просто добавьте класс ExtensionAttribute в пространство имен System.Runtime.CompilerServices, и вы можете использовать методы расширения (конечно, только с С# 3.0).

namespace System.Runtime.CompilerServices
{
    public class ExtensionAttribute : Attribute
    { 
    }
}
6

Доступ к локальным переменным из анонимных методов позволяет обернуть практически любой код с помощью новой логики потока управления, не отвлекая этот код на другой метод. Локальные переменные, объявленные вне метода, доступны внутри метода, такого как локальная переменная endOfLineChar в примере:

http://aaronls.wordpress.com/2010/02/02/retrying-on-exception-conditionally/

6

Это означает, что T должен иметь открытый конструктор без параметров:

 class MyClass<T> where T : new()
 {

 }
  • 1
    Таким образом, вы можете new () это в реализации.
6

Я считаю, что использование условной функции break в Visual Studio очень полезно. Мне нравится, как он позволяет мне установить значение для чего-то, что, например, может быть встречено только в редких случаях, и оттуда я еще смогу изучить код.

6

Прочитав все 9 страниц этого, я почувствовал, что должен указать немного неизвестную особенность...

Это справедливо для .NET 1.1, используя сжатие/декомпрессию в gzipped файлах, нужно было либо:

  • Загрузите ICSharpCode.ZipLib
  • Или обратитесь к библиотеке Java в свой проект и используйте встроенную библиотеку Java, чтобы воспользоваться методами сжатия/декомпрессии GZip.

Недостаточно, о чем я не знал (по-прежнему использовать ICSharpCode.ZipLib еще, даже с .NET 2/3.5), было то, что он был включен в стандартную версию BCL версии 2 вверх, в System.IO.Compression пространство имен... см. страницу MSDN " класс GZipStream".

  • 0
    Если его не переписать, DeflateStream будет больше и более чем на 20% медленнее, чем эквивалент ICSharpCode, поэтому я все еще использую его.
  • 0
    скрытые возможности от c #, потому что ни одна из них не является особенностью c #?
6

Открытые дженерики - это еще одна удобная функция, особенно при использовании Inversion of Control:

container.RegisterType(typeof(IRepository<>), typeof(NHibernateRepository<>));
  • 3
    Что именно это делает?
  • 1
    В этом случае, используя контейнер Unity IoC, это сообщит контейнеру, что он должен разрешить закрытый универсальный конкретный класс NHibernateRepository <T> для любого запрошенного IRepository <T>. Например, var repository = container.Resolve <IRepository <Customer >> (); вернул бы NHibernateRepository <Customer>;
6

Мне особенно нравится нулевое значение DateTime. Поэтому, если у вас есть случаи, когда дана дата и другие случаи, когда дата не указана, я думаю, что это лучше всего использовать, а ИМХО легче понять, используя DateTime.MinValue или что-то еще...

DateTime? myDate = null;

if (myDate.HasValue)
{
    //doSomething
}
else
{
    //soSomethingElse
}
  • 0
    Вы можете заменить .HasValue на == null. Сравнение nullable с нулевым значением myVar == null даст вам истинное значение.
6

Указатели в С#.

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

    string str = "some string";
    Console.WriteLine(str);
    unsafe
    {
        fixed(char *s = str)
        {
            char *c = s;
            while(*c != '\0')
            {
                *c = Char.ToUpper(*c++);                    
            }
        }
    }
    Console.WriteLine(str);

Я бы никогда не делал этого, а только ради этого вопроса, чтобы продемонстрировать эту функцию.

  • 0
    Если вам нужно использовать ключевое слово unsafe, вы делаете это неправильно! :П
  • 4
    Не обязательно ... использование небезопасного кода может значительно повысить производительность, см. " Stackoverflow.com/questions/541331/… "
6

Ключевое слово Yield часто пропускается, когда у него много энергии. Я писал об этом некоторое время назад и обсуждал преимущества (различную обработку) и происходил под капотом доходности, чтобы помочь дать более глубокое понимание.

Использование Yield в С#

  • 0
    +1 не скрыт, но сильно недооценен. Значительно. Недооценивали. (люди принимают это как должное, думая, что это просто для перечисления)
  • 0
    +1 С помощью yield вы мешаете создавать временные списки (из вашего блога)
6

Тип данных может быть определен для перечисления:

enum EnumName : [byte, char, int16, int32, int64, uint16, uint32, uint64]
{
    A = 1,
    B = 2
}
  • 0
    Почему это поможет нам?
  • 0
    однажды я использовал эту функцию, создавая структуру базы данных и маршаллинг
Показать ещё 3 комментария
6

Только что узнав значение инвариантности, ковариации и контравариантности, я обнаружил в и out общие модификаторы, которые будут включены в .NET 4.0. Они кажутся довольно неясными, что большинство программистов не знают о них.

В журнале Visual Studio есть article, в котором обсуждаются эти ключевые слова и как они будут использоваться.

6

Я не мог понять, какие функции используют некоторые из функций класса Convert (например, Convert.ToDouble(int), Convert.ToInt(double)), пока я не объединил их с Array.ConvertAll:

int[] someArrayYouHaveAsInt;
double[] copyOfArrayAsDouble = Array.ConvertAll<int, double>(
                                someArrayYouHaveAsInt,
                                new Converter<int,double>(Convert.ToDouble));

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

int[] someArrayYouHaveAsInt;
double[] copyOfArrayAsDouble = Array.ConvertAll<int, double>(
                                someArrayYouHaveAsInt,
                                new Converter<int,double>(
                                  delegate(int i) { return (double)i; }
                                ));
  • 0
    Я использую Convert.ToDouble, ToInt и т. Д. Все время, не особенно для массивов ... Это действительно удобно, потому что если объект может быть преобразован тем или иным способом, то это произойдет, что не имеет место с приведением типа: например, вы не может привести строку к Double, вы должны использовать Double.Parse. Или переменная типа object, содержащая Decimal, не может быть приведена к int напрямую. Convert.ToDouble или ToInt будет использовать наиболее подходящий способ для преобразования значения, каким бы оно ни было.
6

Я нахожу невероятным, какую проблему переваривает компилятор на сахарный код, используя Внешние переменные:

string output = "helo world!";
Action action = () => Console.WriteLine(output);
output = "hello!";
action();

Это действительно печатает hello!. Зачем? Поскольку компилятор создает вложенный класс для делегата, с общедоступными полями для всех внешних переменных и вставляет установочный код перед каждым вызовом делегату:) Здесь приведен код "рефлексированный":

Action action;
<>c__DisplayClass1 CS$<>8__locals2;
CS$<>8__locals2 = new <>c__DisplayClass1();
CS$<>8__locals2.output = "helo world!";
action = new Action(CS$<>8__locals2.<Main>b__0);
CS$<>8__locals2.output = "hello!";
action();

Довольно круто, я думаю.

  • 0
    Позвольте мне также отметить, что Java, например, даже не позволяет использовать нефинальные внешние переменные в своих анонимных методах ...
  • 0
    Технический термин для этого - «закрытие». Вы не можете поверить, что C # фактически реализует замыкания. Ну, Java-люди не могут в это поверить, но многие другие языки уже имели это раньше, так что, вероятно, это просто отражает ваш опыт в языках.
6

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

AmbientValueAttribute

Это похоже на DefaultValueAttribute, но вместо того, чтобы предоставлять значение, которое по умолчанию имеет значение по умолчанию, оно предоставляет значение, которое свойство использует, чтобы решить, запрашивать ли его значение из другого места. Например, для многих элементов управления в WinForms их свойства ForeColor и BackColor имеют AmbientValue of Color.Empty, чтобы они знали, чтобы получить их цвета из своего родительского элемента управления.

IsolatedStorageSettings

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

Взаимодействие флагов с методами расширения

Используя методы расширения, использование перечисления флагов может быть намного более читаемым.

    public static bool Contains(
          this MyEnumType enumValue,
          MyEnumType flagValue)
    {
        return ((enumValue & flagValue) == flagValue);
    }

    public static bool ContainsAny(
          this MyEnumType enumValue,
          MyEnumType flagValue)
    {
        return ((enumValue & flagValue) > 0);
    }

Это делает проверку корректности и легкости чтения и записи значений флагов. Конечно, было бы лучше, если бы мы могли использовать дженерики и принуждать T к перечислению, но это недопустимо. Возможно, dynamic сделает это проще.

6

Группы методов не известны.

Дано:

Func<Func<int,int>,int,int> myFunc1 = (i, j) => i(j);
Func<int, int> myFunc2 = i => i + 2;

Вы можете сделать это:

var x = myFunc1(myFunc2, 1);

вместо этого:

var x = myFunc1(z => myFunc2(z), 1);
  • 0
    Что то же самое, что сказать, учитывая List<int> items = { 1, 3, 5, 7, 11} ; , что вы можете сделать var total = items.Sum(myFunc2); вместо var total = items.Sum(i => myFunc2(i)); ,
  • 0
    Нет необходимости в лямбдах высшего порядка в базовом примере групп методов
6

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

Пример:

//Place at top of your code
public UseAutoDebug = true;


//Place anywhere in your code including catch areas in try/catch blocks
Debug.Assert(!this.UseAutoDebug);

Просто разместите выше в блоках try/catch или других областях вашего кода и установите UseAutoDebug в true или false и перейдите в debug в любое время, которое вы хотите проверить.

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

Здесь вы можете увидеть функциональный и рабочий пример использования этого метода в этом Шаблоне проектов Visual Studio С#, где он используется в значительной степени:

http://code.msdn.microsoft.com/SEHE

6

Это не особенность С#, но это аддон, который я нахожу очень полезным. Он называется инструментом рефакторинга ресурсов. Это позволяет вам щелкнуть правой кнопкой мыши на литеральной строке и извлечь ее в файл ресурсов. Он будет искать код и находить любые другие литеральные строки, которые соответствуют и заменяют его одним и тем же ресурсом из файла Resx.

http://www.codeplex.com/ResourceRefactoring

6

(я просто использовал это). Установите поле null и верните его без промежуточной переменной:

try
{
    return _field;
}
finally
{
    _field = null;
}
  • 0
    Хороший хак :), в этом коде явно нет «ясного намерения».
  • 1
    Это идиома, как троичный оператор. Попробуйте / наконец-то иметь временные отношения, так что я думаю, что это выглядит довольно неплохо, если вы уже видели это раньше :-)
Показать ещё 2 комментария
6

Микшины - отличная функция. В принципе, mixins позволяют вам иметь конкретный код для интерфейса вместо класса. Затем просто реализуйте интерфейс в кучке классов, и вы автоматически получаете функциональность mixin. Например, чтобы перемещаться в глубоком копировании на несколько классов, определите интерфейс

internal interface IPrototype<T> { }

Добавить функциональность для этого интерфейса

internal static class Prototype
{
  public static T DeepCopy<T>(this IPrototype<T> target)
  {
    T copy;
    using (var stream = new MemoryStream())
    {
      var formatter = new BinaryFormatter();
      formatter.Serialize(stream, (T)target);
      stream.Seek(0, SeekOrigin.Begin);
      copy = (T) formatter.Deserialize(stream);
      stream.Close();
    }
    return copy;
  }
}

Затем реализуем интерфейс любого типа, чтобы получить mixin.

  • 0
    Я понял ... но ... "миксинс" заставляет меня думать о магазине мороженого. Есть ли какой-то другой термин для этого?
  • 0
    Это похоже на то, что делает динамический прокси замка?
Показать ещё 3 комментария
6

Math.Max ​​и Min для проверки границ: Я видел это в большом количестве кода:

if (x < lowerBoundary) 
{
   x = lowerBoundary;
}

Я нахожу это меньше, чище и читаем:

x = Math.Max(x, lowerBoundary);

Или вы также можете использовать тернарный оператор:

x = ( x < lowerBoundary) ? lowerBoundary : x;
  • 1
    Я думаю, что Math.min гораздо более читабелен, чем троичный оператор.
  • 0
    Я лично согласен, но, видимо, не все согласны: refactormycode.com/codes/692-which-is-more-readable-math-or-if
Показать ещё 6 комментариев
6

Вместо использования int.TryParse() или Convert.ToInt32() мне нравится иметь статическую целочисленную функцию синтаксического анализа, которая возвращает значение null, когда он не может разобрать. Тогда я могу использовать?? и тройной оператор, чтобы более четко обеспечить, чтобы моя декларация и инициализация выполнялись на одной линии простым способом.

public static class Parser {
    public static int? ParseInt(string s) {
        int result;
        bool parsed = int.TryParse(s, out result);
        if (parsed) return result;
        else return null;
    }
    // ...
}

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

int x = 0;
YourDatabaseResultSet data = new YourDatabaseResultSet();
if (cond1)
    if (int.TryParse(x_input, x)){
        data = YourDatabaseAccessMethod("my_proc_name", 2, x);
    }
    else{
        x = -1;
        // do something to report "Can't Parse"    
    }
}
else {
    x = y;
    data = YourDatabaseAccessMethod("my_proc_name", 
       new SqlParameter("@param1", 2),
       new SqlParameter("@param2", x));
}

Вы можете сделать:

int x = cond1 ? (Parser.ParseInt(x_input) ?? -1) : y;
if (x >= 0)  data = YourDatabaseAccessMethod("my_proc_name", 
    new SqlParameter("@param1", 2),
    new SqlParameter("@param2", x));

Намного понятнее и понятнее

  • 0
    +1. И вы можете сделать это методом расширения, если не возражаете, чтобы в IntelliSense появился еще один метод для строк
  • 0
    Не могли бы вы просто сделать метод ParseIntOrDefault(string input, int default) ?
Показать ещё 1 комментарий
6

Литералы могут использоваться как переменные этого типа. например.

Console.WriteLine(5.ToString());
Console.WriteLine(5M.GetType());   // Returns "System.Decimal"
Console.WriteLine("This is a string!!!".Replace("!!", "!"));

Просто немного мелочей...

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

Проверенные/непроверенные ключевые слова:

public static int UncheckedAddition(int a, int b)
{
    unchecked { return a + b; }
}

public static int CheckedAddition(int a, int b)
{
    checked { return a + b; } // or "return checked(a + b)";
}

public static void Main() 
{
    Console.WriteLine("Unchecked: " + UncheckedAddition(Int32.MaxValue, + 1));  // "Wraps around"
    Console.WriteLine("Checked: " + CheckedAddition(Int32.MaxValue, + 1));  // Throws an Overflow exception
    Console.ReadLine();
}
6

новый модификатор

Использование "нового" модификатора в С# не совсем скрыто, но это не часто видно. Новый модификатор пригодится, когда вам нужно "скрыть" членов базового класса и не всегда переопределять их. Это означает, что когда вы кладете производный класс в качестве базового класса, тогда "скрытый" метод становится видимым и вызывается вместо того же метода в производном классе.

В коде легче видеть:

public class BaseFoo
{
    virtual public void DoSomething()
    {
        Console.WriteLine("Foo");
    }
}

public class DerivedFoo : BaseFoo
{
    public new void DoSomething()
    {
        Console.WriteLine("Bar");
    }
}

public class DerivedBar : BaseFoo
{
    public override void DoSomething()
    {
        Console.WriteLine("FooBar");
    }
}

class Program
{
    static void Main(string[] args)
    {
        BaseFoo derivedBarAsBaseFoo = new DerivedBar();
        BaseFoo derivedFooAsBaseFoo = new DerivedFoo();

        DerivedFoo derivedFoo = new DerivedFoo();

        derivedFooAsBaseFoo.DoSomething(); //Prints "Foo" when you might expect "Bar"
        derivedBarAsBaseFoo.DoSomething(); //Prints "FooBar"

        derivedFoo.DoSomething(); //Prints "Bar"
    }
}

[Эд: Я получаю дополнительные очки за каламбуры? Извините, не удалось помочь.]

  • 0
    // Печатает «Foo», когда вы можете ожидать «Bar», я думаю, что комментарий говорит само за себя. Программа больше не работает, как ожидалось. Член, скрывающийся таким образом, неразумен. У него нет семантики переопределения. Смотрите: blog.colinmackay.net/archive/2008/10/10/4264.aspx
6

Мысль о @dp AnonCast и решили попробовать немного. Вот то, что я придумал, может быть полезно для некоторых:

// using the concepts of dp AnonCast
static Func<T> TypeCurry<T>(Func<object> f, T type)
{
    return () => (T)f();
}

И вот как это можно использовать:

static void Main(string[] args)
{

    var getRandomObjectX = TypeCurry(GetRandomObject,
        new { Name = default(string), Badges = default(int) });

    do {

        var obj = getRandomObjectX();

        Console.WriteLine("Name : {0} Badges : {1}",
            obj.Name,
            obj.Badges);

    } while (Console.ReadKey().Key != ConsoleKey.Escape);

}

static Random r = new Random();
static object GetRandomObject()
{
    return new {
        Name = Guid.NewGuid().ToString().Substring(0, 4),
        Badges = r.Next(0, 100)
    };
}
  • 0
    Неважно, что он на самом деле делает, я бы нашел использование метода с именем TypeCurry! ; D
6

При чтении книги по разработке .NET framework. Хорошим советом является не использование bool для включения или выключения материала, а использование ENums.

С помощью ENums вы даете себе возможность расширения, не переписывая код, чтобы добавить новую функцию к функции.

  • 0
    и к тому же они более наглядны
  • 0
    И они позволяют вам усложнить неправильное использование вашего API. Представьте метод с кучей логических параметров.
6

Без особого порядка:

Lists<>
Mutex

Новый ярлык определений свойств в Framework 3.5.

6

@Brad Barker

Я думаю, что если вам нужно использовать типы с нулевым значением, лучше использовать Nullable <.T > , а не обозначать знак вопроса. Это делает очевидным очевидное, что магия происходит. Не уверен, почему кто-нибудь когда-нибудь захочет использовать Nullable <.bool > .: -)

Krzysztof Cwalina (один из авторов Framwork Design Guidlines) имеет хорошую должность здесь: http://blogs.msdn.com/kcwalina/archive/2008/07/16/Nullable.aspx

И у Майка Хэдлоу хороший пост на Noodability Voodoo

  • 0
    Не уверен, что согласен с Майком Хэдлоу. Похоже, кто-то, кто провел много времени, используя языки и базы данных без хорошей нулевой поддержки и немного «боится» изменений. Использование нуля в деловых терминах обычно очевидно. Это означает «не знаю» или «не имеет ни одного» (т.е. «не имеет конечной даты»).
  • 0
    «Не уверен, почему кто-то захочет использовать Nullable <.bool>, хотя». Как насчет того, когда вы взаимодействуете со столбцами BIT из базы данных SQL Server? Тип данных BIT в T-SQL может возвращать 1, 0 или NULL и может быть напрямую преобразован в Nullable <Bool> с помощью .NET Nullables чрезвычайно полезны при работе с данными в базах данных, поскольку «типы значений» могут фактически быть NULL.
Показать ещё 2 комментария
5

Nullable.GetValueOrDefault?

  • 1
    или ?? оператор для обнуляемых типов.
5

Разрешены пустые блоки с фигурными скобками.

Вы можете написать такой код

{
    service.DoTonsOfWork(args);
}

Это полезно, если вы хотите попробовать что-то без using или try... finally, которые вы уже написали.

//using(var scope = new TransactionScope)
{
    service.DoTonsOfWork(args);
}
  • 2
    Это также удобно в качестве визуального индикатора других типов вложенности; например, я часто использую фигурные скобки для Debug.Indent() операторов между Debug.Indent() и Debug.Unindent .
  • 2
    они полезны для ограничения области действия переменной
5

Помощники делегатов Action и Func в сочетании с лямбда-методами. Я использую их для простых шаблонов, которым необходим делегат для повышения удобочитаемости. Например, простой шаблон кэширования будет проверять, существует ли запрошенный объект в кеше. Если он существует: верните кешированный объект. Если он не существует, сгенерируйте новый экземпляр, кешируйте новый экземпляр и верните новый экземпляр. Скорее, пишут этот код 1000 раз для каждого объекта, который я могу хранить/извлекать из кеша, я могу написать простой метод шаблона, например...

private static T CachePattern<T>(string key, Func<T> create) where T : class
{
    if (cache[key] == null)
    {
        cache.Add(key, create());
    }

    return cache[key] as T;
}

... то я могу значительно упростить свой кеш-код get/set, используя следующее в моем менеджере кэшей

public static IUser CurrentUser
{
    get
    {
        return CachePattern<IUser>("CurrentUserKey", () => repository.NewUpUser());
    }
}

Теперь простые "повседневные" шаблоны кода могут быть написаны один раз и повторно использованы намного легче ИМХО. Мне не нужно писать тип делегата и выяснить, как я хочу реализовать обратный вызов и т.д. Если я могу написать его через 10 секунд, я гораздо менее склонен. прибегать к вырезанию/вставке простых шаблонов кода, будь то ленивая инициализация и некоторые другие примеры, показанные выше...

  • 0
    Хороший! У нас есть нечто подобное, но с параметром TimeSpan и CacheItemPriority! Мы используем ASP.NET кеш.
5

__ arglist также

[DllImport("msvcrt40.dll")]
public static extern int printf(string format, __arglist);

static void Main(string[] args)
{
   printf("Hello %s!\n", __arglist("Bart"));
}
5

Мне нравится EditorBrowsableAttribute. Он позволяет вам контролировать, отображается ли метод/свойство или нет в Intellisense. Вы можете установить значения Всегда, Дополнительно или Никогда.

Из MSDN...

Примечания

EditorBrowsableAttribute - это подсказка для конструктора, указывающего, должно ли отображаться свойство или метод. Вы можете использовать этот тип в визуальном дизайнере или текстовом редакторе, чтобы определить, что видимо для пользователя. Например, движок IntelliSense в Visual Studio использует этот атрибут, чтобы определить, показывать ли свойство или метод.

В Visual С# вы можете управлять, когда расширенные свойства появляются в IntelliSense и окне свойств с параметром Hide Advanced Members в меню Tools | Варианты | Текстовый редактор | С#. Соответствующий EditorBrowsableState является расширенным.

  • 1
    Однако использование этого атрибута часто раздражает. Используйте его там, где у вас есть член в классе из-за структуры наследования, но член намеренно не реализован. Но не скрывайте участников, которые просто «не рекомендуются для использования». Если есть какой-либо законный сценарий использования участника, оставьте его видимым. Особенно раздражающим является TreeView.Sorted ( msdn.microsoft.com/en-us/library/… ), где свойство Sorted имеет функциональность, но по какой-то странной причине все еще скрыто.
5

Извиняюсь, если это упоминалось, но я использую это много.

Надстройка для Visual Studio была разработана Алексом Пападимулисом. Он используется для вставки обычного текста в виде строки, построителя строк, комментария или области.

http://weblogs.asp.net/alex_papadimoulis/archive/2004/05/25/Smart-Paster-1.1-Add-In---StringBuilder-and-Better-C_2300_-Handling.aspx

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

@

Я знал об этом, но я не знал об использовании двойной цитаты внутри литерала, чтобы избежать цитаты.

Например

string s = "A line of text" + Environment.NewLine + "Another with a \"quote\"!!";

может быть выражена как

string s = @"A line of text 
Another with a ""quote""!!";
  • 0
    Я уверен, что это было опубликовано ранее, и это не совсем скрытая функция, но я не знал о новой строке таким образом.
5

Это не скомпилируется:

namespace ns
{
    class Class1
    {
        Nullable<int> a;
    }
}

Не удалось найти имя типа или пространства имен "Nullable" (вам не хватает директивы using или ссылки на сборку?) < - missing 'using System;'

Но

namespace ns
{
    class Class1
    {
        int? a;
    }
}

будет компилироваться! (.NET 2.0).

  • 1
    принадлежит в самых странных угловых случаях ( stackoverflow.com/questions/194484/… ), но в любом случае +1
  • 0
    Может быть, он будет напрямую конвертировать int? в System.Nullable <System.Int32>, так по какой причине он работает нормально?
Показать ещё 1 комментарий
5

Недавно я недавно узнал, что вы все равно можете вызвать методы с нулевым значением....

Оказывается, когда у вас есть значение с нулевым значением:

decimal? MyValue = null;

где вы могли бы подумать, что вам придется писать:

MyValue == null ? null : MyValue .ToString()

вы можете написать:

MyValue.ToString()

Я знал, что могу вызвать MyValue.HasValue и MyValue.Value... но он не полностью нажал, что я мог бы назвать ToString().

5

Я не видел этого:

for (;;);

То же, что и

while (true) ;
5

Нулевой параметр Lambdas

()=>Console.ReadLine()
  • 1
    Требуется объяснение того, где вы будете использовать это.
  • 0
    В качестве аргумента для любого метода, который принимает параметр делегата Action. В C # 2.0 вы будете использовать делегат {Console.ReadLine ()} вместо этого.
Показать ещё 1 комментарий
5

Мне нравится использовать директиву using, чтобы переименовать некоторые классы для легкого чтения следующим образом:

// defines a descriptive name for a complexed data type
using MyDomainClassList = System.Collections.Generic.List<
  MyProjectNameSpace.MyDomainClass>;

....


MyDomainClassList myList = new MyDomainClassList();
/* instead of 
List<MyDomainClass> myList = new List<MyDomainClass>();
*/

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

using FloatValue  = float; // you only need to change it once to decimal, double...

....
FloatValue val1;
...
5

ФИКСИРОВАННЫЙ/Сила указателей на С# - Эта тема слишком большая, но я просто опишу простые вещи.

В C мы имели объект загрузки структуры, такой как...

struct cType{
   char type[4];
   int  size;
   char name[50];
   char email[100];
}

cType myType;
fread(file, &mType, sizeof(mType));

Мы можем использовать фиксированное ключевое слово в методе "небезопасно", чтобы прочитать выровненную структуру массива байтов.

[Layout(LayoutKind.Sequential, Pack=1)]
public unsafe class CType{
    public fixed byte type[4];
    public int size;
    public fixed byte name[50];
    public fixed byte email[100];
}

Способ 1 (чтение из регулярного потока в буфер байта и отображение байтового массива в отдельные байты структуры)

CType mType = new CType();
byte[] buffer = new byte[Marshal.SizeOf(CType)];
stream.Read(buffer,0,buffer.Length);
// you can map your buffer back to your struct...
fixed(CType* sp = &mType)
{
    byte* bsp = (byte*) sp;
    fixed(byte* bp = &buffer)
    {
         for(int i=0;i<buffer.Length;i++)
         {
             (*bsp) = (*bp);
             bsp++;bp++;
         }
    }
}

Способ 2, вы можете сопоставить Win32 User32.dll ReadFile для прямого чтения байтов...

CType mType = new CType();
fixed(CType* p = &mType)
{
    User32.ReadFile(fileHandle, (byte*) p, Marshal.SizeOf(mType),0);
}
5

Является ли уже цепочка конструкторов?

namespace constructorChain {
    using System;

    public class Class1 {
        public string x;
        public string y;

        public Class1() {
            x = "class1";
            y = "";
        }

        public Class1(string y)
            : this() {
            this.y = y;
        }
    }

    public class Class2 : Class1 {
        public Class2(int y)
            : base(y.ToString()) {

        }
    }
}

...

        constructorChain.Class1 c1 = new constructorChain.Class1();
        constructorChain.Class1 c12 = new constructorChain.Class1("Hello, Constructor!");
        constructorChain.Class2 c2 = new constructorChain.Class2(10);
        Console.WriteLine("{0}:{1}", c1.x, c1.y);
        Console.WriteLine("{0}:{1}", c12.x, c12.y);
        Console.WriteLine("{0}:{1}", c2.x, c2.y);

        Console.ReadLine();
  • 0
    когда это может быть полезно?
  • 3
    Каждый раз, когда у вас есть конструкторы, содержащие один и тот же код инициализации, связывание конструктора позволяет избежать дублирования кода.
5

Я не обнаружил - почти год - то, что Strongly Typed DataRows содержит метод Is [ColumnName] Null().

Например:

Units.UnitsDataTable dataTable = new Units.UnitsDataTable();

foreach (Units.UnitsRow row in dataTable.Rows)
{
    if (row.IsPrimaryKeyNull())
        //....

    if (row.IsForeignKeyNull())
        //....
}
  • 3
    Чтобы быть придирчивым, это не особенность C #, это особенность .NET;)
5

Общий обработчик событий:

public event EventHandler<MyEventArgs> MyEvent;

Таким образом, вы не должны постоянно объявлять своих собственных делегатов,

5

Я считаю этот метод интересным при работе с linqxml:

public bool GetFooSetting(XElement ndef){
   return (bool?)ndef.Element("MyBoolSettingValue") ?? true;
}

в отличие от:

public bool GetFooSetting(XElement ndef){
   return ndef.Element("MyBoolSettingValue") != null ? bool.Parse(ndef.Element("MyBoolSettingValue") ) : true;
}
5

System.Runtime.Remoting.Proxies.RealProxy

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

  • 0
    Как вы получаете АОП от чего-то, что «поддерживает удаленную инфаструктуру»?
  • 0
    Потому что он поддерживает инфраструктуру удаленного взаимодействия, включив AOP;) Посмотрите здесь технические аспекты этого: tdanecker.blogspot.com/2007/09/interception-with-proxies.html
5

Отражают деревья выделения и выражения...

Не пропустите Джеффри Рихтера CLR через С# и Jon Skeet Изображение 953

См. здесь для некоторых ресурсов:

http://www.codeproject.com/KB/trace/releasemodebreakpoint.aspx

http://www.codeproject.com/KB/dotnet/Creating_Dynamic_Types.aspx

http://www.codeproject.com/KB/cs/lambdaexpressions.aspx

5

@lainMH,

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

4

Вы можете объединить protected и internal аксессуар, чтобы сделать его общедоступным в рамках одной и той же сборки, но защищены в другой сборке. Это можно использовать для полей, свойств, метода и даже констант.

4

Я не одобряю это, но я был удивлен, что goto все еще находится вокруг уток входящих снарядов

  • 6
    Я использую goto по случаю. В этом нет ничего плохого.
  • 8
    @ChaosPandion следите за Velociraptors xkcd.com/292
Показать ещё 8 комментариев
4

Это относится к статическим конструкторам. Это метод для статического разрушения (то есть очистки ресурсов при выходе из программы).

Сначала от класса:

class StaticDestructor
{
    /// <summary>
    /// The delegate that is invoked when the destructor is called.
    /// </summary>
    public delegate void Handler();
    private Handler doDestroy;

    /// <summary>
    /// Creates a new static destructor with the specified delegate to handle the destruction.
    /// </summary>
    /// <param name="method">The delegate that will handle destruction.</param>
    public StaticDestructor(Handler method)
    {
        doDestroy = method;
    }

    ~StaticDestructor()
    {
        doDestroy();
    }
}

Затем в качестве члена класса вы хотите иметь "статический деструктор":

private static readonly StaticDestructor destructor = new StaticDestructor
(
    delegate()
    {
        //Cleanup here
    }
);

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

Быстрая и грязная программа, демонстрирующая это поведение:

using System;

namespace TestStaticDestructor
{
    class StaticDestructor
    {
        public delegate void Handler();
        private Handler doDestroy;

        public StaticDestructor(Handler method)
        {
            doDestroy = method;
        }

        ~StaticDestructor()
        {
            doDestroy();
        }
    }

    class SomeClass
    {
        static SomeClass()
        {
            Console.WriteLine("Statically constructed!");
        }

        static readonly StaticDestructor destructor = new StaticDestructor(
            delegate()
            {
                Console.WriteLine("Statically destructed!");
            }
        );
    }

    class Program
    {
        static void Main(string[] args)
        {
            SomeClass someClass = new SomeClass();
            someClass = null;
            System.Threading.Thread.Sleep(1000);
        }
    }
}

Когда программа выйдет, вызывается "статический деструктор".

  • 0
    Просто убедитесь, что все статически «построенные» ссылки являются IDisposable? Если вам это нужно, вы, вероятно, должны рассмотреть Области ограниченного выполнения, SafeHandles, Критические финализаторы или вообще: здесь Теперь, если у вас должна быть детерминированная последовательность выключения, вы не получите иначе, вы могли бы просто подключить свои собственные обработчики выключения к тому же событие, которое вы используете в своем собственном образце, но снова положитесь на IDisposable. Иди посмотри здесь
  • 0
    IDisposable не гарантирует вызова Dispose (), если только вы не включили финализатор для его вызова. Даже в этом случае может существовать некоторый базовый код, который вы должны вызывать во время выключения (например, вызов неуправляемой библиотеки для очистки себя). То есть у вас нет прямого управления ресурсами, которые вы хотите очистить, потому что используемая вами библиотека поддерживает их; Однако SafeHandles также может сделать это для вас.
4

Одна из самых полезных функций Visual Studio - "Сделать идентификатор объекта". Он генерирует идентификатор и "прикрепляет" к объекту, поэтому, когда вы смотрите на объект, вы также увидите идентификатор (независимо от потока).

Во время отладки щелкните правой кнопкой мыши всплывающую подсказку переменной и там она у вас есть. Он также работает с просмотренными/авто/локальными переменными.

4

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

public class User
{
    public long UserId { get; set; }
    public String Name { get; set; }
    public String Password { get; set; }
    public String Email { get; set; }
}

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

User user = new User();
user.UserId = 1;
user.Name = "myname";
etc

Но я узнал следующий способ его инициализации. Я знаю, что разработчикам Visual Basic это понравится, потому что это похоже на оператор, доступный только в VB.NET, а не на С#, который выглядит следующим образом.

User user = new User()
{
    UserId = 1,
    Name = "myname",
    Email = "[email protected]",
    Password = "mypassword"
};
  • 0
    Посмотрите на ответ прямо под вашим - он уже был опубликован (я думаю, не раз).
  • 0
    :) Да, но это выше моего ответа, но это не было кратко, поэтому я внес свой вклад.
Показать ещё 2 комментария
4

Инициализатор коллекции внутри Object Initializer:

MailMessage mail = new MailMessage {
   To = { new MailAddress("[email protected]"), new MailAddress("[email protected]") },
   Subject = "Password Recovery"
};

Вы можете инициализировать целое дерево в одном выражении.

  • 0
    просто не делайте этого в условии использования
4

Я просто хочу упомянуть (из-за OP metioning, где T: struct), что один из компиляторов С# для компиляции - это

where T : Enum

НЕ компилируется. Он выдает ошибку "Ограничение не может быть специальным классом" System.Enum ".

  • 0
    Тем не менее, я держу пари, что where T : System.Enum , с where T : System.Enum E будет.
  • 0
    О, Джон, я действительно имел в виду, что System.Enum не будет компилироваться. Изменил мой ответ на заглавные буквы сейчас.
4
  • Прикрепление ? к типу, чтобы сделать это nullable, ex: int?
  • "c:\dir" вместо @ "C:\dir"
  • 0
    Вы имели в виду "d: \\ dir"?
  • 0
    именно я имею в виду «d: \\» (это правильная форма), но обычно люди хотят написать непосредственно «d: \», но они обязаны использовать \\ вместо того, чтобы они могли использовать дословные строки
4

Преобразование значений перечисления в строковое значение

Учитывая перечисление

enum Country
{
    UnitedKingdom, 
    UnitedStates,
    UnitedArabEmirates,
}

используя его:

public static void PrintEnumAsString( Country country )
{
    Console.Writeline( country.ToString() );
}

будет печатать имя значения перечисления в виде строки, например. "Королевство Соединенное"

  • 9
    Это не уловка или даже скрытая особенность C # или .NET вообще.
4

Мне нравится злоупотреблять тем, что статические шаблонные классы не делят своих статических членов.

Здесь потокобезопасность (во время создания) и дешевая замена на любой Dictionary<Type,...>, когда экземпляр Type известен во время компиляции.

public static class MyCachedData<T>{
    static readonly CachedData Value;
    static MyCachedData(){
       Value=// Heavy computation, such as baking IL code or doing lots of reflection on a type
    }
}

Cheers, Флориан

  • 0
    заменить «любой словарь»? Как? Хотите объяснить?
  • 0
    Привет, похоже, что мое редактирование было вырезано ... Должно было читаться "заменить любой словарь, принимающий тип в качестве ключа, когда тип известен во время компиляции" - исправлено исходное сообщение (использовалось обратное нажатие для удаления скобок)
4

Generics и Любопытно повторяющийся шаблон шаблона действительно помогают с некоторыми объявлениями статического метода/свойства.

Предположим, что вы создаете иерархию классов:

class Base
{
}

class Foo: Base
{
}

class Bar: Base
{
}

Теперь вы хотите объявить статические методы для своих типов, которые должны принимать параметры (или возвращаемые значения) одного и того же типа или статические свойства одного и того же типа. Например, вы хотите:

class Base
{
    public static Base Get()
    {
        // Return a suitable Base.
    }
}

class Foo: Base
{
    public static Foo Get()
    {
        // Return a suitable Foo.
    }
}

class Bar: Base
{
    public static Bar Get()
    {
        // Return a suitable Bar.
    }
}

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

class Base<T> where T: Base<T>
{
    public static T Get<T>()
    {
        // Return a suitable T.
    }
}

а вы Foo и Bar:

class Foo: Base<Foo>
{
}

class Bar: Base<Bar>
{
}

Таким образом, они автоматически получат свои копии статических методов.

Это также творит чудеса, чтобы инкапсулировать шаблон Singleton в базовом классе (я знаю, что приведенный ниже код не является потокобезопасным, это просто для демонстрации точки):

public class Singleton<T> where T: Singleton<T>, new()
{
  public static T Instance { get; private set; }

  static Singleton<T>()
  {
    Instance = new T();
  }
}

Я понимаю, что это заставляет вас иметь открытый конструктор без параметров в вашем одноэлементном подклассе, но нет способа избежать этого во время компиляции без конструкции where T: protected new(); однако для достижения этого можно использовать отражение, чтобы вызвать защищенный/закрытый бездонный конструктор подкласса во время выполнения.

  • 0
    Я думаю, что рефлексия - лучший вариант здесь ... Производительность не очень важна в этом случае, потому что конструктор будет вызываться только один раз, а наличие публичного конструктора для одноэлементного типа просто неправильно.
  • 0
    О да, я полностью согласен. Отражение для защищенных конструкторов в базовом классе Singleton - это то, что я работаю в своем коде. Тем не менее, я не стал загрязнять пример всем кодом отражения.
Показать ещё 5 комментариев
4

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

Как только вы полностью поймете С#, пришло время еще раз понять основы Common Language Infrastructure. Архитектура и основы С#.

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

Ознакомьтесь с этими двумя документами, и вы никогда не станете этим человеком.

4

Как насчет Деревья выражений? Они являются основой LINQ и позволяют выполнять отсроченное выполнение:

Взято из блог Дэвида Хайдена:

В С# 3.0 вы можете определить делегат следующим образом, используя выражение лямбда:

Func<int,int> f = x => x + 1;

Этот делегат скомпилирован в исполняемый код в вашем приложении и может быть вызван как таковой:

var three = f(2); // 2 + 1

Код работает так, как вы ожидали. Ничего особенного здесь.

Деревья выражений

Когда вы определяете делегат как дерево выражений с помощью System.Query.Expression:

Expression<Func<int,int>> expression = x => x + 1;

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

Чтобы фактически использовать делегат, представленный как дерево выражений в вашем приложении, вам придется его компилировать и вызывать в своем приложении:

var originalDelegate = expression.Compile();

var three = originalDelegate.Invoke(2);
4

Общие ограничения:

 //Constructor constraint, T has a default empty constructor
class Node<K,T> where T : new() 
{
}

//Reference\Value Type constraints
//T is a struct
public class MyClass<T> where T : struct 

{...}

//T is a reference type
public class MyClass<T> where T : class 

{...}

public class MyClass<T> where T : SomeBaseClass, ISomeInterface 

{...}
4

Маркетинговые события как несериализуемые:

[field:NonSerializable]
public event SomeDelegate SomeEvent;
4

Возможность создания экземпляра типа на основе общего параметра, такого как

новый T();

  • 1
    Требуется ограничение «где T: new ()».
4

Несколько из меня - сделайте из них то, что вы будете.

Атрибут:

[assembly::InternalsVisibleTo("SomeAssembly")]

Позволяет выставлять внутренние методы/свойства или данные из вашей сборки на другую сборку под названием "SomeAssembly". Все защищенные/личные вещи остаются скрытыми.


Статические конструкторы (иначе называемые "Конструктор типов" )

public MyClass
{
  public static MyClass()
  {
     // type init goes here
  }
  ......
}  

Ключевое слово internal. Настолько полезно во многих отношениях.

4

Если у вас есть текстовое поле поиска на панели инструментов Visual Studio, вы можете ввести " > Program.cs", чтобы открыть файл Program.cs

  • 0
    Это функция Visual Studio, а не функция C #
  • 0
    Также есть фрагменты, но они есть в основном посте в разделе «Возможности Visual Studio».
4

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

4

Функция Framework

Я не знаю, но я был очень удивлен VisualStyleRenderer и весь System.Windows.Forms.VisualStyles-Namespace. Довольно круто!

4

Только что узнал радости [UnmanagedFunctionPointerAttribute(CallingConvention.CDecl)] от попытки взаимодействия с неуправляемой библиотекой функций С++, которая определяла обратные вызовы без __stdcall.

4

Это может быть довольно простым для разработчиков приложений баз данных, но мне потребовалось некоторое время, чтобы понять, что null - это не то же самое, что DBNull.value.

Вам нужно использовать DBNull.value, если вы хотите узнать, является ли значение из записи базы данных нулевым.

4

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

static void Main()
{
    int i;
    unsafe
    {               
        // pointer pi has the address of variable i
        int* pi = &i; 
        // pointer ppi has the address of variable pi
        int** ppi = &pi;
        // ppi(addess of pi) -> pi(addess of i) -> i(0)
        i = 0;
        // dereference the pi, i.e. *pi is i
        Console.WriteLine("i = {0}", *pi); // output: i = 0
        // since *pi is i, equivalent to i++
        (*pi)++;
        Console.WriteLine("i = {0}", *pi); // output: i = 1
        // since *ppi is pi, one more dereference  *pi is i 
        // equivalent to i += 2
        **ppi += 2;
        Console.WriteLine("i = {0}", *pi);// output: i = 3
    }
    Console.ReadLine();
}
4

Пара элементов #region {string} и #endregion очень удобна для группировки кода (выделение).

#region Using statements
using System;
using System.IO;
using ....;
using ....;
#endregion

Блок кода может быть сжат до одной описывающей строки текста. Также работает внутри функций.

  • 2
    Я полагаю, это вопрос предпочтений, но когда я смотрю на исходный код, мне не нужно расширять скрытые разделы кода. Области, используемые экономно (вне методов), не являются проблемой, но остерегайтесь использовать их вместо написания удобочитаемого / поддерживаемого кода.
  • 0
    Я очень люблю регионы, но некоторые люди злоупотребляют ими. Я не мог согласиться с вами больше о том, чтобы держать их подальше от методов. Я видел всю цель метода, обфусцированного областью, которая была свернута по умолчанию, но что коллега случайно скрыл изменение ключевой переменной внутри.
Показать ещё 5 комментариев
4

Метод TryParse для каждого примитивного типа отлично подходит для проверки ввода пользователя.

double doubleValue
if (!Double.TryParse(myDataRow("myColumn"), out doubleValue))
{
    // set validation error
}
  • 0
    Я вижу, что я не единственный, кто забывает "вне" модификатор.
  • 0
    разве это не возвращает бул? и установить с ключевым словом «вне»?
4

Одна интересная вещь, которую я узнал, заключается в том, что различные части фреймворка и языка С# были написаны в разное время, следовательно, несоответствия. Например, сама фреймворк нарушает многие правила FxCop, потому что правила были не все на месте, когда была создана структура.

Кроме того, оператор using был предназначен для деривации "областей", а не для утилизации ресурсов. Это было написано после заявления о блокировке. Eric Gunnerson когда-то упоминалось что-то в этом духе, если бы в начале появился оператор using, им, возможно, не нужно было писать оператор блокировки (хотя кто знает, может быть, они все равно будут), потому что может быть достаточно использовать оператор using.

3

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

  • 3
    Это в .NET Framework (не в самом C #) и на самом деле далеко не скрыто. ;-)
  • 0
    Ну, я почти уверен, что он более скрыт, чем метод String.IsNullOrEmpty, который не в C #, а в .NET Framework. В резюме в начале есть много вещей, не относящихся к C #, но к .NET в целом.
Показать ещё 2 комментария
3

Что касается ссылки post w/perma "Скрытые возможности С#?", есть и другой способ выполнить те же самые разрывы в отступы/строки. Проверьте это.

XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.NewLineOnAttributes = true;
xmlWriterSettings.Indent = true;


XmlWriter xml = XmlWriter.Create(@"C:\file.xml", xmlWriterSettings);

// Start writing the data using xml.WriteStartElement(), xml.WriteElementString(...), xml.WriteEndElement() etc

Я не уверен, что это неизвестная функция!

  • 0
    Особенность языка это, конечно, не
3

Не знаю, является ли это секретом как таковым, но я любил добавленный класс Enumerable (добавляет к IEnumerable) в System.Linq.

http://msdn.microsoft.com/en-us/library/system.linq.enumerable_members.aspx

Пока ключевое слово yield уже указано. Блоки итераторов поражают воображение. Я использовал их для создания списков, которые будут проверяться, чтобы убедиться, что они были совместными. Это в основном позволяет вам идти, хотя функция, которая возвращает значения один за другим и останавливается в любое время.

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

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

3

Я не знал о общих методах, которые могли бы помочь избежать перегрузки метода. Ниже приведены перегруженные методы для печати int и double numbers.

    private static void printNumbers(int [] intNumbers)
    { 
        foreach(int element in intNumbers)
        {
            Console.WriteLine(element);
        }

    }

    private static void printNumbers(double[] doubleNumbers)
    {
        foreach (double element in doubleNumbers)
        {
            Console.WriteLine(element);
        }
    }

Общий метод, который помогает иметь один метод для обоих указанных выше

    private static void printNumbers<E>(E [] Numbers)
    {
        foreach (E element in Numbers)
        {
            Console.WriteLine(element);
        }
    }
3

Свойство "TODO" и список задач

//TODO: [something] 

Добавление этого кода в ваш код (важный интервал) бросает элемент в ваш список задач, а двойной щелчок мышью по элементу переместит вас в нужное место в вашем коде.

  • 5
    Вы также можете определить свои собственные пользовательские теги, я думаю, что большинство людей также используют: BUG, FIX, REFACTOR, HACK Мы также используем: - РУЧНОЙ для отсутствующих подсказок / изменений документации. - ВОПРОС для изменений или кода, который вы хотите узнать мнение команды разработчиков, прежде чем продолжить - РЕСУРС, если код основан на посте блога, статье, каком-то документе (с URL-адресом после него)
  • 1
    На самом деле, это особенность Visual Studio, C # не заботится о таких комментариях или что-то в этом роде.
Показать ещё 1 комментарий
3

Следующий не скрыт, но он совершенно неявный. Я не знаю, были ли опубликованы образцы, подобные приведенным ниже, и я не вижу, есть ли какие-либо выгоды (возможно, их нет), но я попытаюсь показать "странный" код. Следующий пример моделирует оператор for через функторы в С# (делегаты/анонимные делегаты [lambdas]) и замыкания. Другие симуляции, такие как if, if/else, while и do/whle, также моделируются, но я не уверен в switch (возможно, я слишком ленив:)). Я немного сжал исходный код примера, чтобы сделать его более понятным.

private static readonly Action EmptyAction = () => { };
private static readonly Func<bool> EmptyCondition = () => { return true; };

private sealed class BreakStatementException : Exception { }
private sealed class ContinueStatementException : Exception { }
private static void Break() { throw new BreakStatementException(); }
private static void Continue() { throw new ContinueStatementException(); }

private static void For(Action init, Func<bool> condition, Action postBlock, Action statement) {
    init = init ?? EmptyAction;
    condition = condition ?? EmptyCondition;
    postBlock = postBlock ?? EmptyAction;
    statement = statement ?? EmptyAction;
    for ( init(); condition(); postBlock() ) {
        try {
            statement();
        } catch ( BreakStatementException ) {
            break;
        } catch ( ContinueStatementException ) {
            continue;
        }
    }
}

private static void Main() {
    int i = 0; // avoiding error "Use of unassigned local variable 'i'" if not using `for` init block
    For(() => i = 0, () => i < 10, () => i++,
        () => {
            if ( i == 5 )
                Continue();
            Console.WriteLine(i);
        }
    );
}

Если я не ошибаюсь, этот подход довольно похож на практику функционального программирования. Я прав?

  • 3
    На самом деле, нет. Практика функционального программирования означает не просто «заменить все на функции» ... это мышление функционально. Вы все еще пишете цикл, сложный. Большую часть времени вы будете использовать рекурсивную реализацию при функциональном программировании, поскольку это позволит вам декларативно, а не обязательно писать код, я думаю, вы упустили этот момент.
  • 0
    @charkit, спасибо за комментарий. Да, я понял то же самое несколько месяцев назад, и я вижу, что я был несколько неправ, задавая этот вопрос. Однако я написал несколько сценариев для моего плеера Foobar2K, используя простой язык Tagz, и обнаружил, что Tagz ближе к практике функционального программирования, чем пример выше (если я прав в этом случае).
Показать ещё 3 комментария
3

Сначала DebuggerTypeProxy.

[DebuggerTypeProxy(typeof(HashtableDebugView))]
class MyHashtable : Hashtable
{
    private const string TestString = 
        "This should not appear in the debug window.";

    internal class HashtableDebugView
    {
        private Hashtable hashtable;
        public const string TestStringProxy = 
            "This should appear in the debug window.";

        // The constructor for the type proxy class must have a 
        // constructor that takes the target type as a parameter.
        public HashtableDebugView(Hashtable hashtable)
        {
            this.hashtable = hashtable;
        }
    }
}

Вторые:

ICustomTypeDescriptor

3

Не позволяет DataGridView показывать свойство:

[System.ComponentModel.Browsable(false)]
public String LastActionID{get; private set;}

Позволяет настроить дружественный дисплей для компонентов (например, DataGrid или DataGridView):

[System.ComponentModel.DisplayName("Last Action")]
public String LastAction{get; private set;}

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

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    private DataController p_dataSources;
3

Если вы пытаетесь создать строку с разделителями-запятыми из списка элементов:

string[] itemList = { "Example 1", "Example 2", "Example 3" };
CommaDelimitedStringCollection commaStr = new CommaDelimitedStringCollection();
commaStr.AddRange(itemList);
//outputs Example 1,Example 2,Example 3

Посмотрите здесь для другого примера.

  • 12
    Я бы сделал string.Join (",", itemList)
  • 0
    Вырежьте излишки: var cdsc = new CommaDelimitedStringCollection { "Example 1", "Example 2", "Example 3" }; (также строка. Джоин берет торт)
Показать ещё 1 комментарий
3

Определенно типы Func < > при использовании с оператором lambdas в .NET 3.5. Они позволяют настраиваемые функции и могут помочь в создании пользовательских настраиваемых объектов без их подклассификации или при использовании какой-либо ограниченной системы, такой как отслеживание переменной, в которой перечислены, какую кнопку или ключ пользователь хочет контролировать. Кроме того, их можно вызвать так же, как и обычные методы, и их можно назначить как переменные. Единственным недостатком, о котором я могу думать, является то, что вы ограничены 5 аргументами! Хотя к этому моменту вы можете рассмотреть другое решение... Изменить: предоставление некоторых примеров.

...
public Func<InputHelper, float> _horizontalCameraMovement = (InputHelper input) => 
{
    return (input.LeftStickPosition.X * _moveRate) * _zoom;
}
public Func<InputHelper, float> _verticalCameraMovement = (InputHelper input) => 
{
    return (-input.LeftStickPosition.Y * _moveRate) * _zoom;
}
...
public void Update(InputHelper input)
{
    ...
    position += new Vector2(_horizontalCameraMovement(input), _verticalCameraMovement(input));
    ...
}

В этом примере вы можете написать функцию, которая выполняет произвольный расчет, и возвращает float, который будет определять количество движения камеры. Не лучший код, но он имеет смысл.

private int foo;
public int FooProperty {
    get
    {
        if (_onFooGotten() == true)
            return _foo;
    }
    set
    {
        if (onFooSet() == true)
            _foo = value;
    }
}
...
public Func<bool> _onFooGotten = () => 
{
    //do whatever...
    return true;
}
public Func<bool> _onFooSet = () =>
{
    //do whatever...
    return true;
}

Это не лучший пример (поскольку я еще не изучил это использование еще), но он показывает пример использования лямбда-функции для быстрого рейса событий без хлопот делегатов. Изменить: думал о другом. Nullables! Самое близкое, что С# имеет необязательные параметры.

  • 0
    Хорошо, если Func <> так хорош, почему бы вам не привести несколько хороших примеров?
  • 0
    Например, если у вас есть класс 2D-камеры и вы хотите позволить пользователю точно контролировать, как обрабатываются различные аспекты управления (например, перемещение или масштабирование), вы можете использовать Funcs. Или если вы хотите провести мероприятие с одним подписчиком и не хотите беспокоиться об усилиях делегатов. По сути, все, что вы можете придумать, предоставит пользователю больший контроль над вашим объектом без необходимости создавать его подклассы.
Показать ещё 2 комментария
3

Ну... Не используйте его, но многие люди не знают, что С# поддерживает зло goto:)

static void Example()
{
    int i = 0;
top:
    Console.WriteLine(i.ToString());
    if (i == 0)
    {
        i++;
        goto top;
    }
}
  • 0
    Если вы воспользуетесь Reflector, вы увидите, что в коде .NET Framework довольно много ошибок ... страшно!
  • 0
    @Thomas: Большинство gotos, которые вы видите в Reflector, являются просто ошибками Reflector для правильного определения первоначально использованного while, do / while, for и т. Д. В частности, операторы переключения могут привести к «goto Label_01BE» и тому подобному. Если вы будете следовать логическому потоку, вы можете определить, что исходный код, вероятно, лучше структурирован. И теперь, когда исходный код для большей части платформы доступен ( weblogs.asp.net/scottgu/archive/2008/01/16/… ), вы можете убедиться сами.
Показать ещё 4 комментария
3

Только для ссылок - перечисление двоичных операций с использованием метода расширения.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace BinaryOpGenericTest
{
    [Flags]
    enum MyFlags
    {
        A = 1,
        B = 2,
        C = 4

    }

    static class EnumExtensions
    {
        private static Dictionary<Type, Delegate> m_operations = new Dictionary<Type, Delegate>();

        public static bool IsFlagSet<T>(this T firstOperand, T secondOperand) 
                                                  where T : struct
        {

            Type enumType = typeof(T);


            if (!enumType.IsEnum)
            {
                throw new InvalidOperationException("Enum type parameter required");
            }


            Delegate funcImplementorBase = null;
            m_operations.TryGetValue(enumType, out funcImplementorBase);

            Func<T, T, bool> funcImplementor = funcImplementorBase as Func<T, T, bool>;

            if (funcImplementor == null)
            {
                funcImplementor = buildFuncImplementor(secondOperand);
            }



            return funcImplementor(firstOperand, secondOperand);
        }


        private static Func<T, T, bool> buildFuncImplementor<T>(T val)
                                                            where T : struct
        {
            var first = Expression.Parameter(val.GetType(), "first");
            var second = Expression.Parameter(val.GetType(), "second");

            Expression convertSecondExpresion = Expression.Convert(second, typeof(int));
            var andOperator = Expression.Lambda<Func<T, T, bool>>(Expression.Equal(
                                                                                                       Expression.And(
                                                                                                            Expression.Convert(first, typeof(int)),
                                                                                                             convertSecondExpresion),
                                                                                                       convertSecondExpresion),
                                                                                             new[] { first, second });
            Func<T, T, bool> andOperatorFunc = andOperator.Compile();
            m_operations[typeof(T)] = andOperatorFunc;
            return andOperatorFunc;
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            MyFlags flag = MyFlags.A | MyFlags.B;

            Console.WriteLine(flag.IsFlagSet(MyFlags.A));            
            Console.WriteLine(EnumExtensions.IsFlagSet(flag, MyFlags.C));
            Console.ReadLine();
        }
    }
}
3

GetState getters могут быть однострочными.

Использование значения по умолчанию:

public string Caption
{
    get { return (string) (ViewState["Caption"] ?? "Foo"); }
    set { ViewState["Caption"] = value; }
}

public int Index
{
    get { return (int) (ViewState["Index"] ?? 0); }
    set { ViewState["Index"] = value; }
}

Использование значения по умолчанию по умолчанию:

public string Caption
{
    get { return (string) ViewState["Caption"]; }
    set { ViewState["Caption"] = value; }
}

public int? Index
{
    get { return (int?) ViewState["Index"]; }
    set { ViewState["Index"] = value; }
}

Это работает для всего, что поддерживается словарем.

3

ThreadStaticAttribute - мой любимый. Кроме того, полезен атрибут NonSerializableAttribute. (Можете ли вы сказать, что я делаю много работы с сервером, используя удаленный доступ?)

3
double dSqrd = Math.Pow(d,2.0); 

более точна, чем

double dSqrd = d * d; // Here we can lose precision
  • 0
    Хотите привести пример? (Обратите внимание, что всегда есть вероятность потери точности ...)
3

ContextBoundObject

Не так много С#, как вещь .NET. Это еще один способ достижения DI, хотя это может быть трудолюбие. И вы должны унаследовать от него, что может быть отключено.

http://msdn.microsoft.com/en-us/library/system.contextboundobject.aspx

Я использовал его для добавления журнала, когда я украшаю класс/метод специальным атрибутом ведения журнала.

3

Возвращение прогнозов IQueryable

protected void LdsPostings_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{   
    var dc = new MyDataContext();
    var query = dc.Posting.AsQueryable();

    if (isCondition1)
    {
        query = query.Where(q => q.PostedBy == Username);
        e.Result = QueryProjection(query);
        return;
    }

    ...

    if (isConditionN)
    {
        query = query.Where(q => q.Status.StatusName == "submitted");
        query = query.Where(q => q.ReviewedBy == Username);
        e.Result = QueryProjection(query);
        return;
    }
}

и вместо того, чтобы кодировать проекцию несколько раз, создайте один метод:

private IQueryable QueryProjection(IQueryable<Posting> query)
{
    return query.Select(p => new
    {
        p.PostingID,
        p.Category.CategoryName,
        p.Type.TypeName,
        p.Status.StatusName,
        p.Description,
        p.Updated,
        p.PostedBy,
        p.ReviewedBy,
    });
}
2

Большинство P/Invoke - немного странно.

Пример атрибутов:

[DllImport ("gdi32.dll")] 
[return : MarshalAs(UnmanagedType.I4)]
[StructLayout(LayoutKind.Sequential)]
2

Этот трюк для вызова частных методов с использованием Delegate.CreateDelegate очень опрятен.

var subject = new Subject();
var doSomething = (Func<String, String>)
    Delegate.CreateDelegate(typeof(Func<String, String>), subject, "DoSomething");
Console.WriteLine(doSomething("Hello Freggles"));

Здесь контекст, в котором он полезен

2

Другой способ получить IEnumerable через выход без объяснения, создавая объект IEnumerable

public IEnumerable<Request> SomeMethod(IEnumerable<Request> requests)
{
    foreach (Request request in requests)
       yield return DoSomthing(request);
}
  • 1
    конечно образец слабый, и он вряд ли скрыт. Я предпочел бы написать это как requests.Select(DoSomthing) 10 из 10 раз. (Также отредактировал образец, чтобы исправить ошибки кода)
2

Использование @перед строкой, содержащей escape char. В основном, когда физический путь используется для назначения в строковой переменной, каждый использует "\", где символ escape присутствует в строке.

например. string strPath = "D:\websites\web1\images \";

Но escape-символы можно игнорировать, используя @перед строковым значением.

например. string strPath = @ "D:\websites\web1\images \";

2

Не уверен, что Microsoft захочет этот вопрос, особенно с таким количеством ответов. Я уверен, что однажды услышал, как глава Microsoft сказал:

скрытая функция - это потерянная функция

... или что-то в этом роде.

  • 3
    Скрытые функции обнаруживаются теми, кто заботится о мелких деталях.
2

Отбор настолько мощный, когда используется тщательно. Я использовал его в системе шаблонов электронной почты. Менеджер шаблона будет передан объекту, а шаблоны html будут иметь встроенные поля, которые ссылаются на "Свойства", которые могут быть извлечены из переданного объекта с использованием отражения. Работала очень хорошо.

2

При взаимодействии между С++ и С# многие люди не понимают, что С++/CLI - отличный вариант.

Скажем, у вас есть С++ DLL и С# DLL, которая зависит от С++ DLL. Часто самый простой способ заключается в компиляции некоторых (или всех) модулей С++ DLL с помощью/clr-переключателя. Чтобы вызвать вызов С#, С++ DLL - это написать управляемые классы оболочки С++ в С++ DLL. Классы С++/CLI могут вызывать собственный С++-код гораздо более плавным, чем С#, потому что компилятор С++ автоматически генерирует P/invokes для вас, имеет библиотеку специально для взаимодействия, плюс языковые функции для взаимодействия, такие как pin_ptr. И это позволяет управляемому и нативного кода сосуществовать внутри одного и того же двоичного файла.

На стороне С# вы просто вызываете в DLL, как и любой другой .NET файл.

2

Прежде чем лямбда вступает в игру, это анонимный делегат. Это можно использовать для защитного кода, подобного Ruby blockgiven. Я не тестировал, как работает лямбда, потому что я хочу придерживаться .NET 2.0.

Например, если вы хотите, чтобы вы не забыли закрыть свои HTML-теги:

MyHtmlWriter writer=new MyHtmlWriter();
writer.writeTag("html", 
  delegate ()
  { 
    writer.writeTag("head", 
       delegate() 
       { 
           writer.writeTag("title"...);
       }
    )
  })

Я уверен, что лямбда - это вариант, который может дать намного более чистый код:)

  • 1
    если вы используете VS2008, то используйте лямбда-синтаксис. Это синтаксический сахар, а не фреймворк. В любом случае, он скомпилируется с анонимным делегатом, и его не так шумно читать.
2

Некоторые утилиты concurrency в BCL могут квалифицироваться как скрытые функции.

Такие вещи, как System.Threading.Monitor, используются внутри ключевого слова lock; очевидно, что в С# ключевое слово блокировки предпочтительнее, но иногда оно платит, чтобы узнать, как все делается на более низком уровне; Мне пришлось блокировать С++/CLI, поэтому я заключил блок кода с вызовами Monitor.Enter() и Monitor.Exit().

2

Увидел упоминание о List.ForEach выше; 2.0 представил множество операций по набору данных на основе предикатов - Find, FindAll, Exists и т.д. В сочетании с анонимными делегатами вы можете почти добиться простоты 3.5 лямбда-выражений.

2

@Robbie Rocketpants

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

Если вы сделаете бросок, как вы предлагали в примере 1 (используя is и as), он приводит к 2 вызовам оператора "is" . Потому что, когда вы делаете "c = obj как MyClass", сначала он вызывает "is" за кулисами, а затем, если он терпит неудачу, он просто возвращает null.

Если вы делаете бросок, как вы предлагали в примере 2,

c = (MyClass)obj

Затем это на самом деле выполняет операцию "is" , а затем, если это не удается проверить, оно выдает исключение (InvalidCastException).

Итак, если вы хотите сделать легкую динамическую трансляцию, лучше всего сделать третий пример, который вы предоставили:

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

if (c != null)
{
}

против

MyClass c = obj as MyClass;

if (c != null)
{
}

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

2

Свойство PreviousPage:

"System.Web.UI.Page, представляющий страницу, передающую управление текущей странице.

Это очень полезно.

  • 0
    Это не особенность C #, а скорее класс System.Web.UI.Page.
2

Я должен признать, что я не уверен, что это работает лучше или хуже обычного ретранслятора ASP.NET onItemDatabound, но в любом случае здесь мои 5 центов.

MyObject obj = e.Item.DataItem as MyObject;
if(obj != null)
{
  //Do work
}
2

Я думаю, если вам нужно использовать nullable типов, лучше использовать Nullable <.T > а не вопросительный знак нотации. Это делает его очевидно, что магия происходит. Не уверен, почему кто-нибудь захочет использовать Nullable <.bool > хотя.

В веб-службе VB.NET, где параметр не может быть передан (поскольку запрос партнеров не был согласованным или надежным), но ему пришлось пройти проверку на предлагаемый тип (Boolean for "if is search search" ), Отбросьте это до "другого требования руководства"...

... и да, я знаю, что некоторые люди думают, что это не правильный способ сделать это, но IsSearchRequest As Nullable (Of Boolean) спас меня от моего ума в ту ночь!

1

Отдельные статические поля, зависящие от общего типа окружающего класса.

    public class StaticConstrucEx2Outer<T> {

 // Will hold a different value depending on the specicified generic type
 public T SomeProperty { get; set; }

 static StaticConstrucEx2Outer() {
  Console.WriteLine("StaticConstrucEx2Outer " + typeof(T).Name);
 }

 public class StaticConstrucEx2Inner<U, V> {

  static StaticConstrucEx2Inner() {

   Console.WriteLine("Outer <{0}> : Inner <{1}><{2}>",
    typeof(T).Name,
    typeof(U).Name,
    typeof(V).Name);
  }

  public static void FooBar() {}
 }

 public class SCInner {

  static SCInner() {
   Console.WriteLine("SCInner init <{0}>", typeof(T).Name);
  }

  public static void FooBar() {}
 }
}


StaticConstrucEx2Outer<int>.StaticConstrucEx2Inner<string, DateTime>.FooBar();
StaticConstrucEx2Outer<int>.SCInner.FooBar();

StaticConstrucEx2Outer<string>.StaticConstrucEx2Inner<string, DateTime>.FooBar();
StaticConstrucEx2Outer<string>.SCInner.FooBar();

StaticConstrucEx2Outer<string>.StaticConstrucEx2Inner<string, Int16>.FooBar();
StaticConstrucEx2Outer<string>.SCInner.FooBar();

StaticConstrucEx2Outer<string>.StaticConstrucEx2Inner<string, UInt32>.FooBar();

StaticConstrucEx2Outer<long>.StaticConstrucEx2Inner<string, UInt32>.FooBar();

Произведет следующий вывод

Outer <Int32> : Inner <String><DateTime>
SCInner init <Int32>

Outer <String> : Inner <String><DateTime>
SCInner init <String>

Outer <String> : Inner <String><Int16>

Outer <String> : Inner <String><UInt32>

Outer <Int64> : Inner <String><UInt32>
  • 0
    Я могу видеть только статические конструкторы. Ни один из классов не определен как статический ...
  • 0
    @sehe: Мой комментарий был связан с оригинальной вводной строкой - «У вас могут быть экземпляры статических классов». Это было отредактировано через 40 минут после моего комментария, предположительно в ответ
Показать ещё 1 комментарий
1

Когда вам нужно (а) синхронно обмениваться данными между объектами о возникновении события, есть специальный интерфейс ISynchronizeInvoke.

Указание статьи MSDN (ссылка):

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

Вот общая оболочка:

protected void OnEvent<T>(EventHandler<T> eventHandler, T args) where T : EventArgs
{
    if (eventHandler == null) return;

    foreach (EventHandler<T> singleEvent in eventHandler.GetInvocationList())
    {
        if (singleEvent.Target != null && singleEvent.Target is ISynchronizeInvoke)
        {
            var target = (ISynchronizeInvoke)singleEvent.Target;

            if (target.InvokeRequired) {
                target.BeginInvoke(singleEvent, new object[] { this, args });
                continue;
            }
        }
        singleEvent(this, args);
    }
}

и вот пример использования:

public event EventHandler<ProgressEventArgs> ProgressChanged;

private void OnProgressChanged(int processed, int total)
{
    OnEvent(ProgressChanged, new ProgressEventArgs(processed, total));
}
1

Фильтры исключений. Так что "скрытый" вы даже не можете использовать их (по крайней мере, с С#) без патча после компиляции;)

  • 0
    Извините, но это не языковая функция. Это метод программного обеспечения (сродни профилированию, трассировке, ткачимам АОП, верификаторам и т. Д.). Это побочный эффект наличия структурированного набора команд виртуальных машин (например, Perl, Python, Java и CLR)
1

Если вы хотите, чтобы сборщик мусора не запускал финализатор объекта, просто используйте GC.SuppressFinalize(object);. Аналогичным образом, GC.KeepAlive(object); не позволит сборщику мусора собирать этот объект, ссылаясь на него. Не очень часто используется, по крайней мере, в моем опыте, но приятно знать на всякий случай.

1

Не скрыто, но довольно аккуратно. Я нахожу это более сжатой заменой простого if-then-else, который просто присваивает значение на основе условия.

string result = 
              i < 2 ?               //question
              "less than 2" :       //answer
              i < 5 ?               //question
             "less than 5":         //answer   
              i < 10 ?              //question
              "less than 10":       //answer
              "something else";     //default answer 
  • 6
    Однако сравнения выполняются либо в неправильном порядке (т. Е. Сравниваются 2, затем 5, затем 10), либо они представляют собой неправильное направление сравнения (т. Е. Проверка на большее, чем на меньшее, чем). Когда я = 1, он установит результат на «меньше 10».
  • 8
    Это происходит, когда вы используете такую неразборчивую конструкцию.
Показать ещё 3 комментария
1

Вот совет о том, как вы можете использовать директиву #Region для документирования своего кода.

1

Некоторые? странность:)

Delegate target =
  (target0 = target as CallTargetWithContext0) ??
  (target1 = target as CallTargetWithContext1) ??
  (target2 = target as CallTargetWithContext2) ??
  (target3 = target as CallTargetWithContext3) ??
  (target4 = target as CallTargetWithContext4) ??
  (target5 = target as CallTargetWithContext5) ??
  ((Delegate)(targetN = target as CallTargetWithContextN));

Интересно отметить последний бросок, который по какой-то причине необходим. Ошибка или по дизайну?

1

Если разрешены сторонние расширения, C5 и Microsoft CCR (см. это сообщение в блоге для быстрого введения) - обязательное знание.

C5 дополняет .Net несколько не хватает библиотеки коллекций (не Set???), а CCR упрощает параллельное программирование (я слышу его из-за объединения с параллельными расширениями).

0

Я не знаю, является ли это скрытой функцией (""). Любая строковая функция.

  • 0
    "" .func () также работает, но какое хорошее применение мы можем использовать?
  • 1
    Просто быстрый ярлык для нестатических функций
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню