Это пришло мне в голову после того, как я узнал следующее из этого вопроса:
where T : struct
Мы, разработчики С#, все знаем основы С#. Я имею в виду декларации, условные обозначения, циклы, операторы и т.д.
Некоторые из нас даже освоили такие вещи, как Generics, анонимный типы, lambdas, LINQ,...
Но каковы самые скрытые функции или трюки С#, которые даже поклонники С#, наркоманы, эксперты едва ли знают?
yield
от Michael Stumvar
by Майкл Стумusing()
оператор kokosreadonly
kokosas
Майк Стоунas
/is
Эд Свангренas
/is
(улучшено) Rocketpantsdefault
deathofratsglobal::
pzycomanusing()
блокирует AlexCusevolatile
Якуб Штуркextern alias
Якуб ШтуркDefaultValueAttribute
от Michael StumObsoleteAttribute
DannySmurfDebuggerDisplayAttribute
StuDebuggerBrowsable
и DebuggerStepThrough
bdukesThreadStaticAttribute
marxidadFlagsAttribute
Мартин КларкConditionalAttribute
AndrewBurns??
(coalesce nulls) оператор kokoswhere T:new
Lars Mæhlumenum
значения lfoustevent
операторы marxidad?:
) на JasonSchecked
и unchecked
операторы Binoj Antony
Операторы implicit and explicit
Флори__makeref __reftype __refvalue
Иуда Химангоpartial
методы Джон ЭриксонDEBUG
предпроцессорная директива Роберт Дургин__arglist
by Zac BowlingTransactionScope
от KiwiBastardDependantTransaction
от KiwiBastardNullable<T>
IainMHMutex
ДиагоSystem.IO.Path
ageektrappedWeakReference
Хуан МануэльString.IsNullOrEmpty()
метод KiwiBastardList.ForEach()
метод KiwiBastardBeginInvoke()
, EndInvoke()
методы Будет ли деканNullable<T>.HasValue
и Nullable<T>.Value
свойства RismoGetValueOrDefault
метод Джон ШиханЭто не С# per se, но я не видел никого, кто действительно использует System.IO.Path.Combine()
в той степени, в которой они должны. На самом деле весь класс Path действительно полезен, но никто его не использует!
Я готов поспорить, что каждое производственное приложение имеет следующий код, хотя он не должен:
string path = dir + "\\" + fileName;
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, я думаю, что это другая фундаментальная вещь, которую вы должны изучить: -)
Из Рик Стралл:
Вы можете связать?? оператора, чтобы вы могли выполнить нулевое сравнение.
string result = value1 ?? value2 ?? value3 ?? String.Empty;
От CLR через С#:
При нормализации строк это очень рекомендуется использовать ToUpperInvariant вместо ToLowerInvariant, потому что Microsoft имеет оптимизировал код для выполнения сравнение в верхнем регистре.
Я помню, как однажды мой коллега всегда менял строки в верхнем регистре, прежде чем сравнивать. Я всегда задавался вопросом, почему он это делает, потому что я чувствую себя более "естественным", чтобы сначала преобразовать в нижний регистр. После прочтения книги я знаю почему.
Псевдонимы:
using ASimpleName = Dictionary<string, Dictionary<string, List<string>>>;
Это позволяет использовать ASimpleName
вместо Dictionary<string, Dictionary<string, List<string>>>
.
Используйте его, когда вы будете использовать одну и ту же общую большую длинную сложную вещь во многих местах.
Мой любимый трюк использует оператор null coalesce и круглые скобки для автоматического создания экземпляров для меня.
private IList<Foo> _foo;
public IList<Foo> ListOfFoo
{ get { return _foo ?? (_foo = new List<Foo>()); } }
Все остальное, плюс
1) неявные дженерики (почему только по методам, а не по классам?)
void GenericMethod<T>( T input ) { ... }
//Infer type, so
GenericMethod<int>(23); //You don't need the <>.
GenericMethod(23); //Is enough.
2) простые лямбды с одним параметром:
x => x.ToString() //simplify so many calls
3) анонимные типы и инициализаторы:
//Duck-typed: works with any .Add method.
var colours = new Dictionary<string, string> {
{ "red", "#ff0000" },
{ "green", "#00ff00" },
{ "blue", "#0000ff" }
};
int[] arrayOfInt = { 1, 2, 3, 4, 5 };
Другой:
4) Авто свойства могут иметь разные области видимости:
public int MyId { get; private set; }
Спасибо @pzycoman за то, что напомнили мне:
5) Имена псевдонимов (не то, что вам, вероятно, понадобится это конкретное различие):
using web = System.Web.UI.WebControls;
using win = System.Windows.Forms;
web::Control aWebControl = new web::Control();
win::Control aFormControl = new win::Control();
Избегайте проверки нулевых обработчиков событий
Добавление пустого делегата к событиям в объявлении, подавляющее необходимость всегда проверять событие для 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");
}
}
Также см. обсуждение , и этот сообщение в блоге Эрика Липперта на эту тему (и возможные недостатки).
Я не знал ключевое слово "как" довольно долгое время.
MyClass myObject = (MyClass) obj;
против
MyClass myObject = obj as MyClass;
Вторая возвращает null, если obj не является MyClass, а не бросает исключение класса.
Две вещи, которые мне нравятся, - это автоматические свойства, поэтому вы можете еще больше свернуть свой код:
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
становится
public string Name { get; set;}
Инициализаторы объектов:
Employee emp = new Employee();
emp.Name = "John Smith";
emp.StartDate = DateTime.Now();
становится
Employee emp = new Employee {Name="John Smith", StartDate=DateTime.Now()}
Ключевое слово default по умолчанию:
T t = default(T);
приводит к "null", если T является ссылочным типом, а 0, если он является int, false, если он является логическим, и так далее.
Я думаю, что одна из самых недооцененных и менее известных функций С# (.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.
EditValue(someEmployee, e => e.FirstName);
в моей бизнес-логике, и он автоматически генерирует всю логическую схему для ViewModel и View для редактирования этого свойства (таким образом, метка с текстом «Имя» и TextBox с привязкой, которая вызывает установщик свойства FirstName, когда пользователь редактирует имя и обновляет вид с помощью метода получения). Это, кажется, основа для большинства новых внутренних DSL в C #.
Атрибуты вообще, но больше всего DebuggerDisplay. Экономит ваши годы.
" yield" придет мне на ум. Некоторые из таких атрибутов, как [DefaultValue()], также относятся к моим фаворитам.
Ключевое слово var "немного более известно, но вы также можете использовать его в приложениях .NET 2.0 (так долго как вы использовать компилятор .NET 3.5 и установить его для вывода кода 2.0), похоже, не очень хорошо известно.
Редактировать: kokos, спасибо, что указали??? оператора, что действительно действительно полезно. Так как это немного сложно для Google (так как это просто игнорируется), вот страница документации MSDN для этого оператора: ?? Оператор (ссылка на С#)
Команда @сообщает компилятору игнорировать любые escape-символы в строке.
Просто хотел прояснить это... он не говорит ему игнорировать escape-символы, он на самом деле говорит компилятору интерпретировать строку как литерал.
Если у вас
string s = @"cat
dog
fish"
он будет фактически распечатываться как (обратите внимание, что он даже включает пробелы, используемые для отступов):
cat
dog
fish
Вот некоторые интересные скрытые функции С# в виде недокументированных ключевых слов С#:
__makeref
__reftype
__refvalue
__arglist
Это недокументированные ключевые слова С# (даже Visual Studio их распознает!), которые были добавлены для более эффективного бокса/распаковки до дженериков. Они работают в координации с структурой System.TypedReference.
Здесь также __arglist, который используется для списков параметров переменной длины.
Одна вещь, о которой люди мало знают, - это System.WeakReference - очень полезный класс, который отслеживает объект, но все же позволяет сборщик мусора, чтобы собрать его.
Наиболее полезной "скрытой" функцией будет ключевое слово yield yield. Это действительно не скрыто, но многие люди об этом не знают. LINQ построен поверх этого; он позволяет выполнять запросы с задержкой, создавая конечный автомат под капотом. Недавно Раймонд Чен опубликовал информацию о внутренних подробных подробностях.
Я обычно обнаруживаю, что большинство разработчиков С# не знают о типах "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. Проверьте эту ссылку для более подробной информации
Союзы (тип разделяемой памяти С++) в чистом, безопасном С#
Не прибегая к небезопасному режиму и указателям, вы можете иметь членов класса, разделяющих пространство памяти в классе/структуре. Учитывая следующий класс:
[StructLayout(LayoutKind.Explicit)]
public class A
{
[FieldOffset(0)]
public byte One;
[FieldOffset(1)]
public byte Two;
[FieldOffset(2)]
public byte Three;
[FieldOffset(3)]
public byte Four;
[FieldOffset(0)]
public int Int32;
}
Вы можете изменять значения полей байтов, манипулируя полем Int32 и наоборот. Например, эта программа:
static void Main(string[] args)
{
A a = new A { Int32 = int.MaxValue };
Console.WriteLine(a.Int32);
Console.WriteLine("{0:X} {1:X} {2:X} {3:X}", a.One, a.Two, a.Three, a.Four);
a.Four = 0;
a.Three = 0;
Console.WriteLine(a.Int32);
}
Выводит следующее:
2147483647
FF FF FF 7F
65535
просто добавьте используя System.Runtime.InteropServices;
Использование @для имен переменных, которые являются ключевыми словами.
var @object = new object();
var @string = "";
var @if = IpsoFacto();
Если вы хотите выйти из своей программы, не вызывая никаких окончательных блоков или финализаторов, используйте FailFast:
Environment.FailFast()
Возвращение анонимных типов из метода и доступ к элементам без отражения.
// Useful? probably not.
private void foo()
{
var user = AnonCast(GetUserTuple(), new { Name = default(string), Badges = default(int) });
Console.WriteLine("Name: {0} Badges: {1}", user.Name, user.Badges);
}
object GetUserTuple()
{
return new { Name = "dp", Badges = 5 };
}
// Using the magic of Type Inference...
static T AnonCast<T>(object obj, T t)
{
return (T) obj;
}
Здесь полезно использовать регулярные выражения и пути к файлам:
"c:\\program files\\oldway"
@"c:\program file\newway"
@сообщает компилятору игнорировать любые escape-символы в строке.
Примеси. В принципе, если вы хотите добавить функцию к нескольким классам, но не можете использовать один базовый класс для всех из них, получите каждый класс для реализации интерфейса (без участников). Затем напишите метод расширения для интерфейса, т.е.
public static DeepCopy(this IPrototype p) { ... }
Конечно, некоторая ясность приносится в жертву. Но это работает!
Не уверен, почему кто-нибудь когда-нибудь захочет использовать Nullable <bool> .: -)
Правда, False, FileNotFound?
Этот не скрыт настолько, насколько это неправильно.
Большое внимание уделяется алгоритмам "карта", "уменьшение" и "фильтр". Большинство людей не понимают, что .NET 3.5 добавил все три из этих алгоритмов, но он дал им очень SQL-иш имена, основанные на том, что они являются частью LINQ.
"map" = > Выбрать
Преобразовать данные из одной формы в другую"уменьшить" = > Агрегат агрегатов значения в один результат
"filter" = > Где
Фильтры на основе критериев
Возможность использовать LINQ для выполнения встроенной работы над коллекциями, используемыми для выполнения итераций и условностей, может быть невероятно ценной. Стоит узнать, как все методы расширения LINQ могут помочь сделать ваш код более компактным и удобным.
Если вы пытаетесь использовать фигурные скобки внутри выражения String.Format...
int foo = 3;
string bar = "blind mice";
String.Format("{{I am in brackets!}} {0} {1}", foo, bar);
//Outputs "{I am in brackets!} 3 blind mice"
[
и ]
- квадратные скобки, а <
и >
- угловые скобки. См. En.wikipedia.org/wiki/Bracket .
Environment.NewLine
для системных независимых строк.
@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, но мои инстинкты говорят мне, что это сократит максимум до двух типов операций приведения до максимума. Это вряд ли может сломать банк обработки в любом случае; лично, я думаю, что последняя форма выглядит более чистой.
Возможно, это не передовая техника, но я все время вижу, что сводит меня с ума:
if (x == 1)
{
x = 2;
}
else
{
x = 3;
}
можно конденсировать до:
x = (x==1) ? 2 : 3;
Многие люди не понимают, что они могут сравнивать строки, используя: OrdinalIgnoreCase вместо того, чтобы делать someString.ToUpper(). Это устраняет дополнительные накладные расходы на строку.
if( myString.ToUpper() == theirString.ToUpper() ){ ... }
становится
if( myString.Equals( theirString, StringComparison.OrdinalIgnoreCase ) ){ ... }
Только что выученные анонимные типы могут вывести имена свойств из имени переменной:
string hello = "world";
var o = new { hello };
Console.WriteLine(o.hello);
Мне нравится искать вещи в списке вроде: -
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";
}
На практике это не так много, но идея сопоставления предметов с объектом поиска может быть очень полезной и кратким.
Я выбрал это при использовании 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);
См. " Преобразование группы неявных методов в С#" для более.
Вот новый метод строкового класса в С# 4.0:
String.IsNullOrWhiteSpace(String value)
Это о времени.
(myString ?? "").Trim() == ""
Честно говоря, эксперты по самому определению должны знать этот материал. Но чтобы ответить на ваш вопрос: Таблица встроенных типов (ссылка на С#)
Знак компилятора для чисел широко известен для них:
Decimal = M
Float = F
Double = D
// for example
double d = 30D;
Однако они более неясны:
Long = L
Unsigned Long = UL
Unsigned Int = U
Есть больше, но это три очевидные вершины моей головы...
InternalsVisibleTo атрибут - это тот, который не так хорошо известен, но может быть легко доступен в определенные обстоятельства. Это в основном позволяет другой сборке иметь доступ к "внутренним" элементам определяющей сборки.
Dictionary.TryGetValue(клавиша K, выходное значение V)
Работает как чек и входит в один. Вместо <
if(dictionary.ContainsKey(key))
{
value = dictionary[key];
...
}
вы можете просто сделать;
if(dictionary.TryGetValue(key, out value))
{ ... }
и значение установлено.
При отладке вы можете ввести $exception
в окне Watch\QuickWatch\Immediate и получить всю информацию об исключении текущего фрейма. Это очень полезно, если у вас есть исключения 1-го случая!
Применяет различное форматирование к числу в зависимости от того, является ли число положительным, отрицательным или нулевым.
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)
События действительно являются делегатами под капотом, и любой объект-делегат может иметь к нему несколько функций и выделяться из него с помощью операторов + = и-= соответственно.
События также могут управляться с помощью 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;
Больше функции времени исполнения, но я недавно узнал, что есть два сборщика мусора. Рабочая станция gc и сервер gc. Рабочая станция по умолчанию используется для клиентских версий окон, но сервер работает намного быстрее на многоядерных машинах.
<configuration>
<runtime>
<gcServer enabled="true"/>
</runtime>
</configuration>
Будьте осторожны. Сервер gc требует больше памяти.
Не забывайте о перейти.
Другие недоиспользуемые операторы 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
short.MaxValue
?
Использовать "throw" ; вместо "throw ex" ; для сохранения трассировки стека
Если повторное бросание исключения без добавления дополнительной информации, используйте "throw" вместо "throw ex" . Пустой оператор "throw" в блоке catch будет испускать определенный IL, который повторно генерирует исключение, сохраняя исходную трассировку стека. "throw ex" теряет трассировку стека к исходному источнику исключения.
Я не мог видеть это выше - тот, который я не понимал, что вы могли бы сделать до недавнего времени, - это вызвать один конструктор из другого:
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;
}
Я просто хотел скопировать этот код без комментариев. Итак, фокус в том, чтобы просто нажать кнопку 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 и выбираю прямоугольник и не нужно раскомментировать строки.
Проверьте это.
Несколько скрытых функций, с которыми я столкнулся:
stackalloc
, который позволяет выделять массивы в стеке==
и !=
могут быть перегружены, чтобы возвращать типы, отличные от bool
. Странно, но верно.stackalloc
требует небезопасного контекста.
Ключевое слово params, т.е.
public void DoSomething(params string[] theStrings)
{
foreach(string s in theStrings)
{
// Something with the Strings…
}
}
Вызывается как
DoSomething("The", "cat", "sat", "on", "the" ,"mat");
Несколько вещей, которые мне нравятся:
-Если вы создаете интерфейс, похожий на
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(){}
@Давид в Дакоте:
Console.WriteLine( "-".PadRight( 21, '-' ) );
Я использовал это, пока не обнаружил, что класс String имеет конструктор, который позволяет вам сделать то же самое более чистым способом:
new String('-',22);
Статические конструкторы.
Экземпляры:
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
, который является конкретной причиной.
volatile, чтобы сообщить компилятору, что поле может быть изменено несколькими потоками одновременно.
Foreach использует Duck Typing
Перефразируя или бесстыдно крадя из Krzysztof Cwalinas blog об этом. Более интересные мелочи, чем что-либо.
Для поддержки вашего объекта foreach вам не нужно реализовывать IEnumerable. То есть это не ограничение и не проверяется компилятором. Проверено, что
Например,
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!");
}
Add(x)
. public class MyList{ public void Add(string s){} }
. Затем вы можете сделать var l = new MyList{"a", "b", "c"};
...
Add
недостаточно. Из раздела 7.6.10.3 спецификации C #: «Объект коллекции, к которому применяется инициализатор коллекции, должен относиться к типу, который реализует System.Collections.IEnumerable
или возникает ошибка времени компиляции».
С# + 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
using ComplicatedDictionary = Dictionary<int, Dictionary<string, object>>;
ComplicatedDictionary myDictionary = new ComplicatedDictionary();
Поскольку анонимные делегаты были добавлены в 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);
Несколько других атрибутов из пространства имен System.Diagnostics весьма полезны.
DebuggerBrowsable позволит вам скрыть переменные из окна отладчика (мы используем его для всех частных резервных переменных открытых свойств). Наряду с этим DebuggerStepThrough заставляет отладчик перешагивать этот код, очень полезный для немых свойств (возможно, он должен быть преобразован в авто-свойства, если вы можете взять зависимость от компилятора С# 3.0). В качестве примера
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string nickName;
public string NickName {
[DebuggerStepThrough]
get { return nickName; }
[DebuggerStepThrough]
set { this.nickName = value; }
}
Я бы сказал, что использование некоторых системных классов для методов расширения очень удобно, например 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)) {
...
}
Это всего лишь пример, конечно - методы могли бы использовать немного больше работы...
Возможность иметь типы перечисления имеет значения, отличные от int (по умолчанию)
public enum MyEnum : long
{
Val1 = 1,
Val2 = 2
}
Кроме того, тот факт, что вы можете назначить любое числовое значение для этого перечисления:
MyEnum e = (MyEnum)123;
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. Надеюсь, это кому-то поможет.
{ внутри элементов }, {, используя только фигурные скобки }, { > без инструкции управления }.
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
}
Внутри больших, запутанных или архаичных членов (не то, чтобы они когда-либо существовали), это помогает мне предотвратить использование неправильных имен переменных. Сфера применения до более тонких уровней.
Например, этот код записи 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();
(Еще одно использование, чтобы сохранить временную работу из основной области)
Предоставлено Patrik: иногда используется для имитации VB-с-выражением на С#.
var somePerson = this.GetPerson(); // whatever
{
var p = somePerson;
p.FirstName = "John";
p.LastName = "Doe";
//...
p.City = "Gotham";
}
Для проницательного программиста.
Я только что узнал об этом сегодня - и я работаю с С# в течение 5 лет!
Это идентификатор псевдонима пространства имен:
extern alias YourAliasHere;
Вы можете использовать его для загрузки нескольких версий того же типа. Это может быть полезно в сценариях обслуживания или обновления, где у вас есть обновленная версия вашего типа, которая не будет работать в каком-то старом коде, но вам нужно обновить ее до новой версии. Slap на спецификаторе псевдонима пространства имен, и компилятор позволит вам иметь оба типа в вашем коде.
Не скрыт, но я думаю, что многие разработчики не используют свойства HasValue и Value для типов с нулевым значением.
int? x = null;
int y;
if (x.HasValue)
y = x.Value;
Моим любимым является
global::
ключевое слово, чтобы избежать пространства имен с некоторыми нашими сторонними поставщиками кода...
Пример:
global::System.Collections.Generic.List<global::System.String> myList =
new global::System.Collections.Generic.List<global::System.String>();
global
когда используется с квалификатором псевдонима пространства имен ::
ссылается на глобальное пространство имен, которое является пространством имен по умолчанию для любой программы на C #. Пример использования здесь - msdn.microsoft.com/en-us/library/c3ay4x3d.aspx
определение типов
Кто-то опубликовал, что они пропускают typedefs, но вы можете сделать это, как это
using ListOfDictionary = System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, string>>;
и объявите его как
ListOfDictionary list = new ListOfDictionary();
Я прочитал все семь страниц, и я пропустил их:
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)
Я попытаюсь придумать еще...
Ширина в string.Format()
Console.WriteLine("Product: {0,-7} Price: {1,5}", product1, price1);
Console.WriteLine("Product: {0,-7} Price: {1,5}", product2, price2);
производит
Вы можете "использовать" несколько объектов в одном операторе 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))
{ }
Что отличает меня.
Мне нравится ключевое слово continue.
Если вы нажмете условие в цикле и не хотите ничего делать, но продвигайте цикл, просто вставьте "продолжить".
например:.
foreach(object o in ACollection)
{
if(NotInterested)
continue;
}
Инициализация поля по требованию в одной строке:
public StringBuilder Builder
{
get { return _builder ?? (_builder = new StringBuilder()); }
}
Я не уверен, что я чувствую, что С# поддерживает выражения присваивания, но эй, это там: -)
Полный доступ к стеку вызовов:
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
}
}
Итак, если вы возьмете первый, вы знаете, в какой функции вы находитесь. Если вы создаете вспомогательную функцию трассировки - возьмите ее до последней, вы узнаете своего вызывающего.
@lomaxx Я также узнал, что на днях (в то же время я узнал ваш совет), что теперь вы можете иметь разрозненные уровни доступа по одному и тому же свойству:
public string Name { get; private set;}
Таким образом, только сам класс может установить свойство Name.
public MyClass(string name) { Name = name; }
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) + "…" : s;
Программисты, перемещающиеся с C/С++, могут пропустить это:
В С#% (оператор модуля) работает над поплавками!
Свойство Environment.UserInteractive.
Отчеты свойств UserInteractive false для процесса Windows или службы, такой как IIS, которая работает без пользовательский интерфейс. Если это свойство false, не отображать модальные диалоги или сообщений, потому что нет графический пользовательский интерфейс для пользователя для взаимодействия с.
Вложение с помощью выражений
Обычно мы делаем это следующим образом:
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)) {
...
}
Также существует атрибут 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);
}
Два моих личных фаворита, которые я вижу редко:
Легко определить тип, с которым была объявлена переменная (из мой ответ):
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'
Console.WriteLine(iList.GetType().Name);
?
С#?? нулевой коалесцирующий оператор -
Не очень скрытый, но редко используемый. Наверное, потому что многие разработчики пробегают милю, когда видят условное? оператора, поэтому они запускают два, когда видят это. Используется:
string mystring = foo ?? "foo was null"
а не
string mystring;
if (foo==null)
mystring = "foo was null";
else
mystring = foo;
На самом деле это не скрытая функция С#, но я недавно обнаружил класс WeakReference и был потрясен им (хотя это может быть предвзято благодаря тому, что это помогло мне найти решение конкретной проблемы моего...)
AppDomain.UnhandledException Event также является кандидатом на скрытие.
Это событие предоставляет уведомление о неперехваченных исключениях. Он позволяет приложению регистрировать информацию об исключении, прежде чем системный обработчик по умолчанию сообщает об исключении пользователю и завершает работу приложения. Если имеется достаточная информация о состоянии приложения, могут быть предприняты другие действия - например, сохранение данных программы для последующего восстановления. Предупреждение рекомендуется, так как данные программы могут быть повреждены, если исключения не обрабатываются.
Мы видим, что даже на этом сайте многие люди задаются вопросом, почему их приложение не запускается, почему оно разбилось и т.д. Событие AppDomain.UnhandledException
может быть очень полезно для таких случаев, поскольку оно обеспечивает возможность по крайней мере для регистрации причины сбоя приложения.
В С# есть некоторые действительно скрытые ключевые слова и функции, связанные с недокументированным классом 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));
}}
true и false Операторы действительно странные.
Более полный пример можно найти здесь.
Изменить: есть связанный вопрос SO Что такое фальшивый оператор в С# для?
Директива предпроцессора #if DEBUG. Это полезно для тестирования и отладки (хотя я обычно предпочитаю единичный тестовый маршрут).
string customerName = null;
#if DEBUG
customerName = "Bob"
#endif
Он выполнит только код, если для Visual Studio установлено значение скомпилируйте в режиме "Отладка". В противном случае блок кода будет игнорируется компилятором (и неактивен в Visual Studio).
Я не нашел никого, кто использует 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());
if (sb.Length > 0) sb.Append(seperator);
удалить предыдущий разделитель. Вы также хотите кэшировать любые функции Count()
для сохранения переоценок и string.Join()
только для массивов. Как и многие другие разработчики, у меня есть свои собственные методы расширения, которые чище, что string.Join()
Я обнаружил, что об этой функции знают лишь немногие разработчики.
Если вам нужен метод, который работает с переменной 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: Интерфейсы, раздел Об общих и интерфейсных ограничениях.
См. также мой ответ на другой вопрос.
void BoxingMethod(IFoo x)
вызывают бокс, если x
является типом значения. Ваш пример не позволяет вызывать методы интерфейса. Код выше позволяет такие звонки без бокса.
Чарли Калверт объясняет частичные методы в своем блоге
У Скотта Кейта есть приятная демонстрация частичного метода здесь
На основании того, что этот поток должен иметь право "вещи, о которых вы не знали о С# до недавнего времени, несмотря на то, что вы уже все знали", моя личная функция - асинхронные делегаты.
До тех пор, пока я не прочитаю книгу Джеффа Рихтера С#/CLR (отличная книга, все, кто должен ее читать .NET) Я не знал, что вы можете назвать любого делегата, используя BeginInvoke
/EndInvoke
. Я обычно делаю много вызовов ThreadPool.QueueUserWorkItem
(что, как мне кажется, очень похоже на то, что делает делегат BeginInvoke
), но добавление стандартизованного шаблона join/rendezvous иногда может быть действительно полезным.
ОК, это может показаться очевидным, но я хочу упомянуть метод Object.Equals
(статический, с двумя аргументами).
Я уверен, что многие люди даже не знают об этом или забывают об этом, но в некоторых случаях это может действительно помочь. Например, если вы хотите сравнить два объекта для равенства, не зная, являются ли они нулевыми. Сколько раз вы писали что-то вроде этого:
if ((x == y) || ((x != null && y != null) && x.Equals(y)))
{
...
}
Когда вы можете просто написать:
if (Object.Equals(x, y))
{
...
}
(Object.Equals
фактически реализуется точно так же, как в первом примере кода)
Как насчет IObservable?
Практически все знают IEnumerable, но их математическое двойное кажется неизвестным IObservable. Может быть, потому, что его новый в .NET 4.
Что он делает, вместо того, чтобы извлекать информацию (например, перечислимую), она передает информацию подписчику (наблюдателям).
Вместе с расширения Rx он изменит, как мы имеем дело с событиями. Чтобы проиллюстрировать, насколько мощным является проверка очень короткого примера здесь.
Каждый видел его. В основном, когда вы видите это:
[assembly: ComVisible(false)]
Часть "assembly:" этого атрибута является целевой. В этом случае атрибут применяется к сборке, но есть и другие:
[return: SomeAttr]
int Method3() { return 0; }
В этом примере атрибут применяется к возвращаемому значению.
Один класс, который мне нравится, это System.Xml.XmlConvert
, который можно использовать для чтения значений из тега xml. Особенно, если я читаю логическое значение из атрибута xml или элемента, я использую
bool myFlag = System.Xml.XmlConvert.ToBoolean(myAttribute.Value);
Примечание: поскольку логический тип в xml принимает 1 и 0 в дополнение к "true" и "false" в качестве допустимых значений, использование сравнения строк в этом случае подвержено ошибкам.
Несколько человек упомянули использование блоков, но я думаю, что они намного полезнее, чем люди поняли. Подумайте о них как об инструменте AOP бедного человека. У меня есть множество простых объектов, которые фиксируют состояние в конструкторе, а затем восстанавливают его в методе Dispose(). Это позволяет мне обернуть часть функциональности в используемом блоке и убедиться, что состояние будет восстановлено в конце. Например:
using(new CursorState(this, BusyCursor));
{
// Do stuff
}
CursorState фиксирует текущий курсор, используемый формой, затем устанавливает форму для использования курсора. В конце он восстанавливает исходный курсор. Я делаю такие вещи, например, захватывая выбор и текущую строку на сетке перед обновлением и т.д.
Я опаздываю на эту вечеринку, поэтому мои первые выборы уже приняты. Но я не видел, чтобы кто-нибудь упоминал об этом камне:
Параллельные расширения .NET Framework
У него есть такие вещи, как замена с помощью Parallel.For или foreach с Parallel.ForEach
Параллельный пример:
Как вы думаете, сколько объектов CLR может быть создано за одну секунду?
См. приведенный ниже пример:
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);
}
}
}
Рядом со всеми крутыми были упомянуты. Не уверен, хорошо ли это известно или нет.
Инициализация конструктора свойств/объекта С#:
var foo = new Rectangle()
{
Fill = new SolidColorBrush(c),
Width = 20,
Height = 20
};
Это создает прямоугольник и устанавливает перечисленные свойства.
Я заметил что-то смешное - у вас может быть запятая в конце списка свойств, без синтаксической ошибки. Так что это также верно:
var foo = new Rectangle()
{
Fill = new SolidColorBrush(c),
Width = 20,
Height = 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: удалена временная переменная внутри метода расширения на основе комментариев
temp
. Он используется для предотвращения асинхронного изменения, но для этого достаточно параметра event
.
Извинения за публикацию настолько поздно, что я новичок в 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.
string.Empty
Я знаю, что это не фантастический (смехотворно странный), но я использую его все время вместо "".
И это довольно хорошо скрыто, пока кто-то не говорит вам об этом.
string.Empty
более «правильный». Для справки .Empty
не совсем редко используемое значение, большинство типов значений реализуют его, и рекомендации Microsoft поощряют его использование (и действительно, статические версии общих значений, MinValue
и MaxValue
- два других общих значения). Это может предотвратить ненужное создание дублированных типов значений, когда может использоваться один и тот же экземпляр (типы неизменяемых значений).
Не совсем скрыто, но полезно. Когда у вас есть enum
с flags
, вы можете использовать shift-left, чтобы сделать вещи более ясными. например.
[Flags]
public enum ErrorTypes {
None = 0,
MissingPassword = 1 << 0,
MissingUsername = 1 << 1,
PasswordIncorrect = 1 << 2
}
С# 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 */
});
}
Я вижу, что многие люди реплицируют функциональность Nullable<T>.GetValueOrDefault(T)
.
Мой любимый атрибут: InternalsVisibleTo
На уровне сборки вы можете объявить, что другая сборка может видеть ваши внутренности. Для целей тестирования это совершенно замечательно.
Вставьте это в свой AssemblyInfo.cs или его эквивалент, и ваша тестовая сборка получит полный доступ ко всем внутренним материалам, требующим тестирования.
[assembly: InternalsVisibleTo("MyLibrary.Test, PublicKey=0024...5c042cb")]
Как вы можете видеть, тестовая сборка должна иметь сильное имя, чтобы получить доверие тестируемой сборки.
Доступно в .Net Framework 2.0+, Compact Framework 2.0+ и XNA Framework 1.0 +.
Вам нужно вернуть пустой IEnumerable?
public IEnumerable<T> GetEnumerator(){
yield break;
}
System.Linq.Enumerable.Empty<T>()
return new T[0];
, Есть много способов сделать это. Это всего лишь один. Но это интересный ...
Вы можете ограничить срок службы и, следовательно, область переменных, используя скобки { }
.
{
string test2 = "3";
Console.Write(test2);
}
Console.Write(test2); //compile error
test2
живет только в скобках.
ключевое слово extern для ссылки на две версии сборок с одинаковыми именами полного типа.
Мне нравится использовать символ @для SQL-запросов. Он сохраняет sql хорошо и отформатирован и без необходимости окружать каждую строку разделителем строк.
string sql = @"SELECT firstname, lastname, email
FROM users
WHERE username = @username AND password = @password";
Вы можете хранить цвета в Enum.
public enum MyEnumColors : uint
{
Normal = 0xFF9F9F9F,
Active = 0xFF8EA98A,
Error = 0xFFFF0000
}
Возможность использования выражений 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'
Более подробную информацию можно найти на Как узнать переменную или имя параметра в С#?
исправлено выражение
Это утверждение запрещает сборщику мусора перемещать подвижную переменную. Исправлено также можно использовать для создания буферов фиксированного размера.
Фиксированный оператор устанавливает указатель на управляемую переменную и "выдает" эту переменную во время выполнения инструкции.
В стеке stackalloc выделяется блок памяти.
Также полезно, но не часто используется: Ограниченные области выполнения.
Цитата из блога BCL Team:
Ограниченные области выполнения (CER) существуют, чтобы помочь разработчику написать ей чтобы поддерживать согласованность. CLR не гарантирует, что код верен, но CLR поднять все вызванные работой среды (например, асинхронные исключения) либо до запуска кода, либо после его завершения. В сочетании с ограничения на то, что разработчик может положить в ССВ, это полезный способ предоставления сильных гарантий относительно будет ли выполняться ваш код. ЦЭИ жадно подготовлены, что означает, что когда мы увидим один, мы с нетерпением ждем JIT любой код, найденный в его статически обнаруживаемый граф вызовов. Если хост CLR заботится о стеке переполнения, мы будем исследовать некоторую сумму пространства стека (хотя, возможно, недостаточно места для стека для любого произвольный метод *). Мы также задерживаем нить прерывается до тех пор, пока CER не будет законченный ход.
Это может быть полезно при внесении изменений в несколько полей структуры данных атомным способом. Таким образом, это помогает иметь транзакции по объектам.
Также CriticalFinalizerObject кажется скрытым (по крайней мере, кто не пишет небезопасный код). CriticalFinalizerObject гарантирует, что сбор мусора выполнит финализатор. При распределении финализатор и его график вызовов готовятся заранее.
Использование оператора ~ ~ с атрибутом 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.
}
Вы можете добавлять и удалять делегаты с меньшим количеством ввода.
Обычный способ:
handler += new EventHandler(func);
Меньше ввод текста:
handler += func;
Вы можете использовать любой символ Юникода в именах С#, например:
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!";
}
}
Как насчет FlagsAttribute в перечислении? Он позволяет выполнять побитовые операции... взял меня навсегда, чтобы узнать, как делать побитовые операции в .NET красиво.
Мне часто приходится сталкиваться с необходимостью использования универсального параметра-объекта в представлении в базовом классе.
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, упрощения сортировки и т.д.
Я просто говорю, что дженерики могут быть невероятно мощным зверем в чужих руках.
Вы можете поместить несколько атрибутов в одну пару квадратных скобок:
[OperationContract, ServiceKnownType(typeof(Prism)), ServiceKnownType(typeof(Cuboid))]
Shape GetShape();
Когда класс реализует 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);
}
}
}
При определении пользовательских атрибутов вы можете использовать их с [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 {}
Позволяет сообщить компилятору опустить вызов метода, помеченного атрибутом при определенных условиях (#define).
Тот факт, что вызов метода опущен, также означает, что его параметры не оцениваются. Это очень удобно, и это позволяет вам вызывать дорогостоящие функции проверки в Debug.Assert() и не беспокоиться о том, что они замедляют вашу сборку релизов.
упрощенное 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();
Этот подход может быть вложенным, как и в цикле.
Я не знаю, было ли это уже опубликовано (я просмотрел первое сообщение, не смог найти его).
Это лучше всего показать на примере, предполагая, что у вас есть этот класс (для имитации кортежа), в попытке продемонстрировать все возможности языка, которые делают это возможным, я буду проходить его шаг за шагом.
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 во всех случаях!
Я становлюсь большим поклонником методов расширения, поскольку они могут добавить много желаемых функций к существующему коду или коду, которые вы не можете редактировать. Один из моих фаворитов я добавляю во все, что я делаю сейчас, для 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");
}
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
Как насчет этого:
#if DEBUG
Console.Write("Debugging");
#else
Console.Write("Final");
#endif
Когда ваше решение скомпилировано с определением DEBUG, оно выведет "Отладка".
Если ваш компилятор настроен на Release, он напишет "Final".
Я не стал по-настоящему ценить "использование" блоков до недавнего времени. Они делают вещи намного более аккуратными:)
Лямбда-выражения
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] );
}
Пара, о которой я могу думать:
[field: NonSerialized()]
public EventHandler event SomeEvent;
Это предотвращает сериализацию события. "Поле:" указывает, что атрибут должен быть применен к полю поддержки событий.
Еще одна известная функция - переопределение обработчиков событий add/remove:
public event EventHandler SomeEvent
{
add
{
// ...
}
remove
{
// ...
}
}
Вы можете использовать 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);
}
}
То же самое с аргументом, который унаследован от базового класса и интерфейса.
Что-то я пропустил в течение долгого времени: вы можете сравнивать строки с помощью
"string".equals("String", StringComparison.InvariantCultureIgnoreCase)
вместо выполнения:
"string".ToLower() == "String".ToLower();
Падение через 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;
}
Настройте методы расширения на null
; это не приведет к выбросу NullReferenceException
.
Пример приложения: вы можете определить альтернативу для ToString()
под названием ToStringOrEmpty()
, которая вернет пустую строку при вызове null
.
Синтаксис делегата эволюционировал по последовательным версиям С#, но мне все еще сложно их запомнить. К счастью, делегаты Action<>
и Func<>
легко запоминаются.
Например:
Action<int>
- это метод делегата, который принимает один аргумент int и возвращает void.Func<int>
- это метод делегата, который не принимает аргументов и возвращает int.Func<int, bool>
- это метод делегата, который принимает один аргумент int и возвращает bool.Эти функции были представлены в версии 3.5.NET framework.
Существуют операторы для выполнения implicit
и explicit
пользовательское преобразование типов между объявленным классом и одним или несколькими произвольными классами. Оператор implicit
эффективно позволяет моделировать перегрузку оператора присваивания, что возможно в таких языках, как С++, но не С#.
Кажется, что это не очень часто встречающаяся функция, но она фактически используется в LINQ to XML ( System.Xml.Linq
), где вы можете неявно преобразовывать строки в объекты XName
. Пример:
XName tagName = "x:Name";
Я обнаружил эту функцию в этой статье о том, как имитировать множественное наследование в С#.
Я еще не могу прокомментировать, но обратите внимание, что по умолчанию Visual Studio 2008 автоматически переходит к свойствам, поэтому в этом случае атрибут DebuggerStepThrough больше не нужен.
Кроме того, я не заметил, чтобы кто-нибудь показывал, как объявить lambda без параметров (полезно для реализации Action < > )
() => DoSomething(x);
Вы должны также прочитать о закрытии - я недостаточно умен, чтобы правильно объяснить их. Но в основном это означает, что компилятор делает умные вещи так, что x в этой строке кода будет работать, даже если он выходит из области видимости после создания лямбда.
Недавно я также обнаружил, что вы можете притворяться, что игнорируете параметр лямбда:
(e, _) => DoSomething(e)
Это не игнорирует его, просто _ является допустимым идентификатором. Таким образом, вы не можете игнорировать оба параметра, как это, но я думаю, что это своего рода аккуратный способ указать, что мы не заботимся об этом параметре (обычно EventArgs, который является .Empty
).
Мне нравится, что я могу использовать LINQ для объектов на простом .NET 2.0 (т.е. без необходимости устанавливать .NET 3.5 везде). Все, что вам нужно, это реализация всех методов расширения запроса. См. LINQBridge
Вы можете изменить схему округления, используя:
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
Вложенные классы могут обращаться к закрытым членам внешнего класса.
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);
}
}
Эти функции позволяют использовать некоторые интересные возможности для обеспечения доступа к определенным членам через несколько скрытых классов.
Оператор присваивания Or
довольно хорош. Вы можете написать это:
x |= y
вместо этого:
x = x | y
Это часто бывает практичным, если вам нужно переменная или свойство (x
в примере), которое начинается с false
, но вы хотите изменить его на значение какой-либо другой логической переменной/свойство только тогда, когда это другое значение true
.
Не уверен, что этот вопрос уже упоминался, но атрибут ThreadStatic является очень полезным. Это статическое поле статично только для текущего потока.
[ThreadStatic]
private static int _ThreadStaticInteger;
Вы не должны включать инициализатор, потому что он выполняется только один раз для всего приложения, вам лучше сделать поле нулевым и проверить, имеет ли значение значение null до его использования.
И еще одна вещь для потоков приложений ASP.NET используется повторно, поэтому, если вы измените значение, которое может быть использовано для другого запроса страницы.
Тем не менее, я нашел это полезным несколько раз. Например, при создании настраиваемого класса транзакций, который:
using (DbTransaction tran = new DbTransaction())
{
DoQuery("...");
DoQuery("...");
}
Конструктор DbTransaction задает поле ThreadStatic самому себе и сбрасывает его в null в методе dispose. DoQuery проверяет статическое поле и if!= Null использует текущую транзакцию, если не по умолчанию используется другое. Нам не нужно передавать транзакцию каждому методу плюс он упрощает перенос других методов, которые не были оригинальны, которые должны использоваться с транзакцией внутри транзакции...
Только одно использование:)
Одна из вещей, о которых многие не знают, - это некоторые из директив препроцессора С#. Вы можете использовать #error This is an error.
для генерации ошибки компилятора и #warning This is a warning.
Я обычно использую их, когда я разрабатываю подход сверху вниз как список "todo". Я буду #error Implement this function
или #warning Eventually implement this corner case
в качестве напоминания.
TrueForAll-метод List<T>
:
List<int> s = new List<int> { 6, 1, 2 };
bool a = s.TrueForAll(p => p > 0);
Чтобы вызвать конструктор базового класса, просто поместите 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()
Чтобы проверить, является ли IEnumerable <T>
пустым с LINQ, используйте:
IEnumerable
<T>
.Any();
<T>
.Count()!= 0)...
<T>
.<T>
.FirstOrDefault() == null)...
<T>
.Any() является самым кратким и работает наилучшим образом.Мне очень нравятся неявные общие параметры для функций. Например, если у вас есть:
public void DoStuff<T>(T value);
Вместо того, чтобы называть его следующим образом:
DoStuff<int>(5);
Вы можете:
DoStuff(5);
И он будет определять общий тип из типа параметра.
Работа с перечислениями.
Преобразование строки в Enum:
enum MyEnum
{
FirstValue,
SecondValue,
ThirdValue
}
string enumValueString = "FirstValue";
MyEnum val = (MyEnum)Enum.Parse(typeof(MyEnum), enumValueString, true)
При сравнении переменных типа enum вам не нужно указывать int:
MyEnum val = MyEnum.SecondValue;
if (val < MyEnum.ThirdValue)
{
// Do something
}
IEnumerable
SelectMany
, который выравнивает список списков в один список. Скажем, у меня есть список Orders
, и каждый Order
имеет список LineItems
в этом порядке.
Я хочу узнать, сколько всего продано LineItems
...
int totalItems = Orders.Select(o => o.LineItems).SelectMany(i => i).Sum();
Интерпретация строк. Это тот, который я еще не видел в этой дискуссии. Это немного неясно, но в определенных условиях это может быть полезно.
CLR хранит таблицу ссылок на литеральные строки (и программно интернированные строки). Если вы используете одну и ту же строку в нескольких местах вашего кода, она будет храниться один раз в таблице. Это может облегчить объем памяти, необходимый для выделения строк.
Вы можете проверить, является ли строка интернированной, используя String.IsInterned(string), и вы можете ставить строку, используя String.Intern(строка).
Примечание. CLR может содержать ссылку на интернированную строку после приложения или даже на конец AppDomain. Подробнее см. Документацию MSDN.
System.Diagnostics.Debug.Assert (false);
вызовет всплывающее окно и позволит вам приложить отладчик к запущенному процессу .NET во время выполнения. Очень полезно в те моменты, когда по какой-то причине вы не можете напрямую отлаживать приложение ASP.NET.
Директивы препроцессора могут быть отличными, если вы хотите различное поведение между режимами Debug и Release.
С 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();
}
Инициализаторы словарей всегда полезны для быстрых хаков и модульных тестов, где вам нужно жестко кодировать некоторые данные.
var dict = new Dictionary<int, string> { { 10, "Hello" }, { 20, "World" } };
С# позволяет добавлять методы настройки свойств к конкретным типам, которые реализуют свойства 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;
Foo
?
Выражение для инициализации словаря в С# 3.5:
new Dictionary<string, Int64>() {{"Testing", 123}, {"Test", 125}};
Вместо того, чтобы делать что-то дрянное, как это:
Console.WriteLine("{0} item(s) found.", count);
Я использую следующий встроенный трюк:
Console.WriteLine("{0} item{1} found.", count, count==1 ? "" : "s");
Это будет отображаться "элемент", когда есть один элемент или "элементы", когда есть больше (или меньше), чем 1. Не так много усилий для небольшого профессионализма.
Вы можете включить строку!
switch(name)
{
case "Dave":
return true;
case "Bob":
return false;
default:
throw new ApplicationException();
}
Очень удобно! и намного чище, чем куча инструкций if-else
Не конкретная вещь С#, но я - наркоман из тернарных операций.
Вместо
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);
Это помогает писать краткий код, но вложенные проклятые вещи могут быть неприятными, и их можно использовать для зла, но тем не менее я люблю маленьких присосок
Я уверен, что все знакомы с перегрузкой оператора, но, возможно, некоторые из них не являются.
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);
Не уверен, что это было упомянуто или нет (11 страниц!)
Но атрибут OptionalField
для классов поражает, когда вы выполняете сериализацию классов/объектов версий.
http://msdn.microsoft.com/en-us/library/ms229752(VS.80).aspx
Я читал книгу "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
Есть много случаев, когда мне не нужно проверять, есть ли у моего словаря ключ или нет, и я просто хочу добавить соответствующую пару "ключ/значение" (если необходимо, переписать существующую пару ключ/значение). к этому открытию я всегда должен был проверить, существовал ли ключ до его добавления.
Мне нравится
#if DEBUG
//Code run in debugging mode
#else
//Code run in release mode
#endif
Возможность использовать LINQ для выполнения встроенной работы над коллекциями, используемыми для выполнения итераций и условностей, может быть невероятно ценной. Стоит узнать, как все методы расширения LINQ могут помочь сделать ваш код более компактным и удобным.
Четыре неудобства переключения от Eric Lippert
Я не думаю, что кто-то упомянул о том, что добавляет? после того, как имя типа значения сделает его нулевым.
Вы можете сделать:
DateTime? date = null;
DateTime - это структура.
Nullable<T>
не является скрытой функцией ...
[field: NonSerialized]
public event EventHandler Event;
Таким образом, прослушиватель событий не сериализуется.
Просто [NonSerialized] не работает, поскольку NonSerializedAttribute может применяться только к полям.
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();
Вы можете создавать делегаты из методов расширения, как если бы они были обычными методами, выполняя параметр 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"
Обратите внимание, что это не будет работать с методами расширения, которые расширяются тип значения; см. этот вопрос.
SomeEvent += OtherEvent.Raise
, где Raise
- это пользовательский метод расширения. Разница между этим и SomeEvent += OtherEvent
оставлена читателю как упражнение (оно существенное). Я могу написать в блоге об этом в какой-то момент.
Уже упомянутые атрибуты DebuggerDisplay и DebuggerBrowsable контролируют видимость элементов и отображаемое текстовое значение. Простое переопределение ToString() приведет к тому, что отладчик будет использовать вывод этого метода.
Если вам нужен более сложный вывод, вы можете использовать/создать Debugger Visualizer, несколько примеров доступны здесь.
Microsoft предоставляет расширение отладчика, известное как SOS. Это чрезвычайно мощное (хотя и часто запутанное) расширение, которое является отличным способом диагностики "утечек", более точных нежелательных ссылок на объекты, которые больше не требуются.
Следуя этим инструкциям вы сможете пройти через источник некоторых частей фреймворка.
В Visual Studio 2010 несколько улучшений и новых функций:
Свойства, отображаемые при просмотре компонентов. Свойства в режиме проектирования:
private double _Zoom = 1;
[Category("View")]
[Description("The Current Zoom Level")]
public double Zoom
{
get { return _Zoom;}
set { _Zoom = value;}
}
Делает вещи намного проще для других пользователей ваших библиотек компонентов.
Инициализация массива без указания типа элемента массива:
var pets = new[] { "Cat", "Dog", "Bird" };
string[] pets = {"Cat", "Dog", "Bird"};
Встроенный (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 ", потому что это не тип делегата".
Использование ключевого слова по умолчанию в родовом коде для возврата значения по умолчанию для типа.
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;
}
}
Здесь я недавно нашел, что было полезно:
Microsoft.VisualBasic.Logging.FileLogTraceListener
Это реализация TraceListener, которая имеет множество функций, таких как автоматическое переполнение файла журнала, которое я ранее использовал для пользовательской среды ведения журнала. Приятно, что это ключевая часть .NET и интегрирована с инфраструктурой Trace, поэтому ее легко подобрать и использовать сразу.
Это "скрыто", потому что оно находится в сборке Microsoft.VisualBasic... но вы также можете использовать его из С#.
Прохладный трюк для эмуляции функциональных аргументов "подстановочные знаки" (например, "_" в Haskell) при использовании lambdas:
(_, b, __) => b.DoStuff(); // only interested in b here
У вас могут быть общие методы в не-общий класс.
динамическое ключевое слово в С# 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
}
}
Это не тип С#, но я только что нашел интерфейсы 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, и это слишком плохо.
Все еще - ОЧЕНЬ скрыто.:)
Явная реализация члена интерфейса, в которой реализован элемент интерфейса, но скрыт, если экземпляр не передан типу интерфейса.
Относительно foreach
: он не использует "утиную печать", так как утиная печать ИМО относится к проверке времени выполнения. Он использует проверку структурного типа (в отличие от номинального) во время компиляции для проверки требуемого метода в типе.
Определяет, являются ли указанные экземпляры объекта одним и тем же экземпляром.
Параметры:
Пример:
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 были переопределены для сравнения содержимого строки.
См. также этот ответ на другой вопрос ( "Как проверить нули в перегрузке оператора '== без бесконечной рекурсии?" ).
Вы вводите "prop", а затем дважды нажмите [TAB], он генерирует полезный код для ваших свойств и может ускорить ввод.
Я знаю, что это работает в VS 2005 (я использую его), но я не знаю в предыдущих версиях.
В дополнение к ответу duncansmart, также методы расширения могут использоваться в Framework 2.0. Просто добавьте класс ExtensionAttribute
в пространство имен System.Runtime.CompilerServices, и вы можете использовать методы расширения (конечно, только с С# 3.0).
namespace System.Runtime.CompilerServices
{
public class ExtensionAttribute : Attribute
{
}
}
Доступ к локальным переменным из анонимных методов позволяет обернуть практически любой код с помощью новой логики потока управления, не отвлекая этот код на другой метод. Локальные переменные, объявленные вне метода, доступны внутри метода, такого как локальная переменная endOfLineChar
в примере:
http://aaronls.wordpress.com/2010/02/02/retrying-on-exception-conditionally/
Это означает, что T должен иметь открытый конструктор без параметров:
class MyClass<T> where T : new()
{
}
Я считаю, что использование условной функции break в Visual Studio очень полезно. Мне нравится, как он позволяет мне установить значение для чего-то, что, например, может быть встречено только в редких случаях, и оттуда я еще смогу изучить код.
Прочитав все 9 страниц этого, я почувствовал, что должен указать немного неизвестную особенность...
Это справедливо для .NET 1.1, используя сжатие/декомпрессию в gzipped файлах, нужно было либо:
Недостаточно, о чем я не знал (по-прежнему использовать ICSharpCode.ZipLib еще, даже с .NET 2/3.5), было то, что он был включен в стандартную версию BCL версии 2 вверх, в System.IO.Compression пространство имен... см. страницу MSDN " класс GZipStream".
Открытые дженерики - это еще одна удобная функция, особенно при использовании Inversion of Control:
container.RegisterType(typeof(IRepository<>), typeof(NHibernateRepository<>));
Мне особенно нравится нулевое значение DateTime. Поэтому, если у вас есть случаи, когда дана дата и другие случаи, когда дата не указана, я думаю, что это лучше всего использовать, а ИМХО легче понять, используя DateTime.MinValue
или что-то еще...
DateTime? myDate = null;
if (myDate.HasValue)
{
//doSomething
}
else
{
//soSomethingElse
}
Указатели в С#.
Их можно использовать для обработки строк на месте. Это небезопасная функция, поэтому ключевое слово 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);
Я бы никогда не делал этого, а только ради этого вопроса, чтобы продемонстрировать эту функцию.
Ключевое слово Yield часто пропускается, когда у него много энергии. Я писал об этом некоторое время назад и обсуждал преимущества (различную обработку) и происходил под капотом доходности, чтобы помочь дать более глубокое понимание.
Тип данных может быть определен для перечисления:
enum EnumName : [byte, char, int16, int32, int64, uint16, uint32, uint64]
{
A = 1,
B = 2
}
Только что узнав значение инвариантности, ковариации и контравариантности, я обнаружил в и out общие модификаторы, которые будут включены в .NET 4.0. Они кажутся довольно неясными, что большинство программистов не знают о них.
В журнале Visual Studio есть article, в котором обсуждаются эти ключевые слова и как они будут использоваться.
Я не мог понять, какие функции используют некоторые из функций класса 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; }
));
Я нахожу невероятным, какую проблему переваривает компилятор на сахарный код, используя Внешние переменные:
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();
Довольно круто, я думаю.
Я так опаздываю на этот вопрос, но я хотел добавить несколько, которые, я думаю, не были покрыты. Это не С# -специфичные, но я думаю, что они заслуживают упоминания для любого разработчика С#.
DefaultValueAttribute
, но вместо того, чтобы предоставлять значение, которое по умолчанию имеет значение по умолчанию, оно предоставляет значение, которое свойство использует, чтобы решить, запрашивать ли его значение из другого места. Например, для многих элементов управления в WinForms их свойства ForeColor
и BackColor
имеют AmbientValue
of Color.Empty
, чтобы они знали, чтобы получить их цвета из своего родительского элемента управления.
Используя методы расширения, использование перечисления флагов может быть намного более читаемым.
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
сделает это проще.
Группы методов не известны.
Дано:
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);
List<int> items = { 1, 3, 5, 7, 11} ;
, что вы можете сделать var total = items.Sum(myFunc2);
вместо var total = items.Sum(i => myFunc2(i));
,
Я называю это 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 С#, где он используется в значительной степени:
Это не особенность С#, но это аддон, который я нахожу очень полезным. Он называется инструментом рефакторинга ресурсов. Это позволяет вам щелкнуть правой кнопкой мыши на литеральной строке и извлечь ее в файл ресурсов. Он будет искать код и находить любые другие литеральные строки, которые соответствуют и заменяют его одним и тем же ресурсом из файла Resx.
(я просто использовал это). Установите поле null и верните его без промежуточной переменной:
try
{
return _field;
}
finally
{
_field = null;
}
Микшины - отличная функция. В принципе, 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.
Math.Max и Min для проверки границ: Я видел это в большом количестве кода:
if (x < lowerBoundary)
{
x = lowerBoundary;
}
Я нахожу это меньше, чище и читаем:
x = Math.Max(x, lowerBoundary);
Или вы также можете использовать тернарный оператор:
x = ( x < lowerBoundary) ? lowerBoundary : x;
Вместо использования 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));
Намного понятнее и понятнее
ParseIntOrDefault(string input, int default)
?
Литералы могут использоваться как переменные этого типа. например.
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();
}
новый модификатор
Использование "нового" модификатора в С# не совсем скрыто, но это не часто видно. Новый модификатор пригодится, когда вам нужно "скрыть" членов базового класса и не всегда переопределять их. Это означает, что когда вы кладете производный класс в качестве базового класса, тогда "скрытый" метод становится видимым и вызывается вместо того же метода в производном классе.
В коде легче видеть:
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"
}
}
[Эд: Я получаю дополнительные очки за каламбуры? Извините, не удалось помочь.]
Мысль о @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)
};
}
При чтении книги по разработке .NET framework. Хорошим советом является не использование bool для включения или выключения материала, а использование ENums.
С помощью ENums вы даете себе возможность расширения, не переписывая код, чтобы добавить новую функцию к функции.
Без особого порядка:
Lists<>
Mutex
Новый ярлык определений свойств в Framework 3.5.
@Brad Barker
Я думаю, что если вам нужно использовать типы с нулевым значением, лучше использовать Nullable <.T > , а не обозначать знак вопроса. Это делает очевидным очевидное, что магия происходит. Не уверен, почему кто-нибудь когда-нибудь захочет использовать Nullable <.bool > .: -)
Krzysztof Cwalina (один из авторов Framwork Design Guidlines) имеет хорошую должность здесь: http://blogs.msdn.com/kcwalina/archive/2008/07/16/Nullable.aspx
И у Майка Хэдлоу хороший пост на Noodability Voodoo
Nullable.GetValueOrDefault?
??
оператор для обнуляемых типов.
Разрешены пустые блоки с фигурными скобками.
Вы можете написать такой код
{
service.DoTonsOfWork(args);
}
Это полезно, если вы хотите попробовать что-то без using
или try... finally
, которые вы уже написали.
//using(var scope = new TransactionScope)
{
service.DoTonsOfWork(args);
}
Debug.Indent()
операторов между Debug.Indent()
и Debug.Unindent
.
Помощники делегатов 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 секунд, я гораздо менее склонен. прибегать к вырезанию/вставке простых шаблонов кода, будь то ленивая инициализация и некоторые другие примеры, показанные выше...
__ arglist также
[DllImport("msvcrt40.dll")]
public static extern int printf(string format, __arglist);
static void Main(string[] args)
{
printf("Hello %s!\n", __arglist("Bart"));
}
Мне нравится EditorBrowsableAttribute. Он позволяет вам контролировать, отображается ли метод/свойство или нет в Intellisense. Вы можете установить значения Всегда, Дополнительно или Никогда.
Из MSDN...
Примечания
EditorBrowsableAttribute - это подсказка для конструктора, указывающего, должно ли отображаться свойство или метод. Вы можете использовать этот тип в визуальном дизайнере или текстовом редакторе, чтобы определить, что видимо для пользователя. Например, движок IntelliSense в Visual Studio использует этот атрибут, чтобы определить, показывать ли свойство или метод.
В Visual С# вы можете управлять, когда расширенные свойства появляются в IntelliSense и окне свойств с параметром Hide Advanced Members в меню Tools | Варианты | Текстовый редактор | С#. Соответствующий EditorBrowsableState является расширенным.
Извиняюсь, если это упоминалось, но я использую это много.
Надстройка для Visual Studio была разработана Алексом Пападимулисом. Он используется для вставки обычного текста в виде строки, построителя строк, комментария или области.
В этом плагине (я также не знаю, упоминалось ли это) я заметил, что строки вставляются с префиксом строкового литерала:
@
Я знал об этом, но я не знал об использовании двойной цитаты внутри литерала, чтобы избежать цитаты.
Например
string s = "A line of text" + Environment.NewLine + "Another with a \"quote\"!!";
может быть выражена как
string s = @"A line of text
Another with a ""quote""!!";
Это не скомпилируется:
namespace ns
{
class Class1
{
Nullable<int> a;
}
}
Не удалось найти имя типа или пространства имен "Nullable" (вам не хватает директивы using или ссылки на сборку?) < - missing 'using System;
'
Но
namespace ns
{
class Class1
{
int? a;
}
}
будет компилироваться! (.NET 2.0).
Недавно я недавно узнал, что вы все равно можете вызвать методы с нулевым значением....
Оказывается, когда у вас есть значение с нулевым значением:
decimal? MyValue = null;
где вы могли бы подумать, что вам придется писать:
MyValue == null ? null : MyValue .ToString()
вы можете написать:
MyValue.ToString()
Я знал, что могу вызвать MyValue.HasValue и MyValue.Value... но он не полностью нажал, что я мог бы назвать ToString().
Я не видел этого:
for (;;);
То же, что и
while (true) ;
Нулевой параметр Lambdas
()=>Console.ReadLine()
Мне нравится использовать директиву 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;
...
ФИКСИРОВАННЫЙ/Сила указателей на С# - Эта тема слишком большая, но я просто опишу простые вещи.
В 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);
}
Является ли уже цепочка конструкторов?
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();
Я не обнаружил - почти год - то, что 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())
//....
}
Общий обработчик событий:
public event EventHandler<MyEventArgs> MyEvent;
Таким образом, вы не должны постоянно объявлять своих собственных делегатов,
Я считаю этот метод интересным при работе с 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;
}
System.Runtime.Remoting.Proxies.RealProxy
Он позволяет аспектно ориентированное программирование на С#, и вы также можете делать с ним много других причудливых вещей.
Отражают деревья выделения и выражения...
Не пропустите Джеффри Рихтера CLR через С# и Jon Skeet
См. здесь для некоторых ресурсов:
http://www.codeproject.com/KB/trace/releasemodebreakpoint.aspx
http://www.codeproject.com/KB/dotnet/Creating_Dynamic_Types.aspx
@lainMH,
Необязательные логические значения полезны при извлечении значений из базы данных, которые могут быть обнуляемы и при возврате значений. Иногда вы хотите знать, что поле не установлено.
Вы можете объединить protected
и internal
аксессуар, чтобы сделать его общедоступным в рамках одной и той же сборки, но защищены в другой сборке. Это можно использовать для полей, свойств, метода и даже констант.
Я не одобряю это, но я был удивлен, что goto все еще находится вокруг уток входящих снарядов
goto
по случаю. В этом нет ничего плохого.
Это относится к статическим конструкторам. Это метод для статического разрушения (то есть очистки ресурсов при выходе из программы).
Сначала от класса:
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);
}
}
}
Когда программа выйдет, вызывается "статический деструктор".
Одна из самых полезных функций Visual Studio - "Сделать идентификатор объекта". Он генерирует идентификатор и "прикрепляет" к объекту, поэтому, когда вы смотрите на объект, вы также увидите идентификатор (независимо от потока).
Во время отладки щелкните правой кнопкой мыши всплывающую подсказку переменной и там она у вас есть. Он также работает с просмотренными/авто/локальными переменными.
Я немного опаздываю в этом разговоре, и я хотел бы внести свой вклад в следующее. Это может быть что-то новое для некоторых разработчиков.
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"
};
Инициализатор коллекции внутри Object Initializer:
MailMessage mail = new MailMessage {
To = { new MailAddress("[email protected]"), new MailAddress("[email protected]") },
Subject = "Password Recovery"
};
Вы можете инициализировать целое дерево в одном выражении.
Я просто хочу упомянуть (из-за OP metioning, где T: struct), что один из компиляторов С# для компиляции - это
where T : Enum
НЕ компилируется. Он выдает ошибку "Ограничение не может быть специальным классом" System.Enum ".
where T : System.Enum
, с where T : System.Enum
E будет.
Преобразование значений перечисления в строковое значение
Учитывая перечисление
enum Country
{
UnitedKingdom,
UnitedStates,
UnitedArabEmirates,
}
используя его:
public static void PrintEnumAsString( Country country )
{
Console.Writeline( country.ToString() );
}
будет печатать имя значения перечисления в виде строки, например. "Королевство Соединенное"
Мне нравится злоупотреблять тем, что статические шаблонные классы не делят своих статических членов.
Здесь потокобезопасность (во время создания) и дешевая замена на любой 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, Флориан
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()
; однако для достижения этого можно использовать отражение, чтобы вызвать защищенный/закрытый бездонный конструктор подкласса во время выполнения.
Многое из этого объясняется уже в стандарте. Он хорошо читается как для новичков, так и для экспертов, его много читать, но это официальный стандарт, и он наполнен сочными деталями.
Как только вы полностью поймете С#, пришло время еще раз понять основы Common Language Infrastructure. Архитектура и основы С#.
Я встречал множество программистов, которые не знают разницы между объектом и ValueType, кроме тех ограничений, которые придерживаются.
Ознакомьтесь с этими двумя документами, и вы никогда не станете этим человеком.
Как насчет Деревья выражений? Они являются основой 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);
Общие ограничения:
//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
{...}
Маркетинговые события как несериализуемые:
[field:NonSerializable]
public event SomeDelegate SomeEvent;
Возможность создания экземпляра типа на основе общего параметра, такого как
новый T();
Несколько из меня - сделайте из них то, что вы будете.
Атрибут:
[assembly::InternalsVisibleTo("SomeAssembly")]
Позволяет выставлять внутренние методы/свойства или данные из вашей сборки на другую сборку под названием "SomeAssembly". Все защищенные/личные вещи остаются скрытыми.
Статические конструкторы (иначе называемые "Конструктор типов" )
public MyClass
{
public static MyClass()
{
// type init goes here
}
......
}
Ключевое слово internal
. Настолько полезно во многих отношениях.
Если у вас есть текстовое поле поиска на панели инструментов Visual Studio, вы можете ввести " > Program.cs", чтобы открыть файл Program.cs
InternalsVisibleToAttribute указывает, что типы, которые обычно видны только внутри текущей сборки, видны другой сборке. Статья в msdn
Функция Framework
Я не знаю, но я был очень удивлен VisualStyleRenderer и весь System.Windows.Forms.VisualStyles-Namespace. Довольно круто!
Только что узнал радости [UnmanagedFunctionPointerAttribute(CallingConvention.CDecl)]
от попытки взаимодействия с неуправляемой библиотекой функций С++, которая определяла обратные вызовы без __stdcall.
Это может быть довольно простым для разработчиков приложений баз данных, но мне потребовалось некоторое время, чтобы понять, что null - это не то же самое, что DBNull.value.
Вам нужно использовать DBNull.value, если вы хотите узнать, является ли значение из записи базы данных нулевым.
Я думаю, что многие люди знают о указателях в 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 = π
// 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();
}
Пара элементов #region {string} и #endregion очень удобна для группировки кода (выделение).
#region Using statements
using System;
using System.IO;
using ....;
using ....;
#endregion
Блок кода может быть сжат до одной описывающей строки текста. Также работает внутри функций.
Метод TryParse для каждого примитивного типа отлично подходит для проверки ввода пользователя.
double doubleValue
if (!Double.TryParse(myDataRow("myColumn"), out doubleValue))
{
// set validation error
}
Одна интересная вещь, которую я узнал, заключается в том, что различные части фреймворка и языка С# были написаны в разное время, следовательно, несоответствия. Например, сама фреймворк нарушает многие правила FxCop, потому что правила были не все на месте, когда была создана структура.
Кроме того, оператор using был предназначен для деривации "областей", а не для утилизации ресурсов. Это было написано после заявления о блокировке. Eric Gunnerson когда-то упоминалось что-то в этом духе, если бы в начале появился оператор using, им, возможно, не нужно было писать оператор блокировки (хотя кто знает, может быть, они все равно будут), потому что может быть достаточно использовать оператор using.
В последнее время я узнал о методе String.Join. Это действительно полезно при создании строк, например столбцов, для выбора по запросу.
Что касается ссылки 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
Я не уверен, что это неизвестная функция!
Не знаю, является ли это секретом как таковым, но я любил добавленный класс 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
Я не знал о общих методах, которые могли бы помочь избежать перегрузки метода. Ниже приведены перегруженные методы для печати 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);
}
}
Свойство "TODO" и список задач
//TODO: [something]
Добавление этого кода в ваш код (важный интервал) бросает элемент в ваш список задач, а двойной щелчок мышью по элементу переместит вас в нужное место в вашем коде.
Следующий не скрыт, но он совершенно неявный. Я не знаю, были ли опубликованы образцы, подобные приведенным ниже, и я не вижу, есть ли какие-либо выгоды (возможно, их нет), но я попытаюсь показать "странный" код. Следующий пример моделирует оператор 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);
}
);
}
Если я не ошибаюсь, этот подход довольно похож на практику функционального программирования. Я прав?
Сначала 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;
}
}
}
Вторые:
Не позволяет 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;
Если вы пытаетесь создать строку с разделителями-запятыми из списка элементов:
string[] itemList = { "Example 1", "Example 2", "Example 3" };
CommaDelimitedStringCollection commaStr = new CommaDelimitedStringCollection();
commaStr.AddRange(itemList);
//outputs Example 1,Example 2,Example 3
Посмотрите здесь для другого примера.
var cdsc = new CommaDelimitedStringCollection { "Example 1", "Example 2", "Example 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! Самое близкое, что С# имеет необязательные параметры.
Ну... Не используйте его, но многие люди не знают, что С# поддерживает зло goto:)
static void Example()
{
int i = 0;
top:
Console.WriteLine(i.ToString());
if (i == 0)
{
i++;
goto top;
}
}
Только для ссылок - перечисление двоичных операций с использованием метода расширения.
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();
}
}
}
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; }
}
Это работает для всего, что поддерживается словарем.
ThreadStaticAttribute - мой любимый. Кроме того, полезен атрибут NonSerializableAttribute. (Можете ли вы сказать, что я делаю много работы с сервером, используя удаленный доступ?)
double dSqrd = Math.Pow(d,2.0);
более точна, чем
double dSqrd = d * d; // Here we can lose precision
ContextBoundObject
Не так много С#, как вещь .NET. Это еще один способ достижения DI, хотя это может быть трудолюбие. И вы должны унаследовать от него, что может быть отключено.
http://msdn.microsoft.com/en-us/library/system.contextboundobject.aspx
Я использовал его для добавления журнала, когда я украшаю класс/метод специальным атрибутом ведения журнала.
Возвращение прогнозов 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,
});
}
Большинство P/Invoke - немного странно.
Пример атрибутов:
[DllImport ("gdi32.dll")]
[return : MarshalAs(UnmanagedType.I4)]
[StructLayout(LayoutKind.Sequential)]
Этот трюк для вызова частных методов с использованием Delegate.CreateDelegate очень опрятен.
var subject = new Subject();
var doSomething = (Func<String, String>)
Delegate.CreateDelegate(typeof(Func<String, String>), subject, "DoSomething");
Console.WriteLine(doSomething("Hello Freggles"));
Другой способ получить IEnumerable через выход без объяснения, создавая объект IEnumerable
public IEnumerable<Request> SomeMethod(IEnumerable<Request> requests)
{
foreach (Request request in requests)
yield return DoSomthing(request);
}
requests.Select(DoSomthing)
10 из 10 раз. (Также отредактировал образец, чтобы исправить ошибки кода)
Использование @перед строкой, содержащей escape char. В основном, когда физический путь используется для назначения в строковой переменной, каждый использует "\", где символ escape присутствует в строке.
например. string strPath = "D:\websites\web1\images \";
Но escape-символы можно игнорировать, используя @перед строковым значением.
например. string strPath = @ "D:\websites\web1\images \";
Не уверен, что Microsoft захочет этот вопрос, особенно с таким количеством ответов. Я уверен, что однажды услышал, как глава Microsoft сказал:
скрытая функция - это потерянная функция
... или что-то в этом роде.
Отбор настолько мощный, когда используется тщательно. Я использовал его в системе шаблонов электронной почты. Менеджер шаблона будет передан объекту, а шаблоны html будут иметь встроенные поля, которые ссылаются на "Свойства", которые могут быть извлечены из переданного объекта с использованием отражения. Работала очень хорошо.
При взаимодействии между С++ и С# многие люди не понимают, что С++/CLI - отличный вариант.
Скажем, у вас есть С++ DLL и С# DLL, которая зависит от С++ DLL. Часто самый простой способ заключается в компиляции некоторых (или всех) модулей С++ DLL с помощью/clr-переключателя. Чтобы вызвать вызов С#, С++ DLL - это написать управляемые классы оболочки С++ в С++ DLL. Классы С++/CLI могут вызывать собственный С++-код гораздо более плавным, чем С#, потому что компилятор С++ автоматически генерирует P/invokes для вас, имеет библиотеку специально для взаимодействия, плюс языковые функции для взаимодействия, такие как pin_ptr. И это позволяет управляемому и нативного кода сосуществовать внутри одного и того же двоичного файла.
На стороне С# вы просто вызываете в DLL, как и любой другой .NET файл.
Прежде чем лямбда вступает в игру, это анонимный делегат. Это можно использовать для защитного кода, подобного Ruby blockgiven. Я не тестировал, как работает лямбда, потому что я хочу придерживаться .NET 2.0.
Например, если вы хотите, чтобы вы не забыли закрыть свои HTML-теги:
MyHtmlWriter writer=new MyHtmlWriter();
writer.writeTag("html",
delegate ()
{
writer.writeTag("head",
delegate()
{
writer.writeTag("title"...);
}
)
})
Я уверен, что лямбда - это вариант, который может дать намного более чистый код:)
Некоторые утилиты concurrency в BCL могут квалифицироваться как скрытые функции.
Такие вещи, как System.Threading.Monitor, используются внутри ключевого слова lock; очевидно, что в С# ключевое слово блокировки предпочтительнее, но иногда оно платит, чтобы узнать, как все делается на более низком уровне; Мне пришлось блокировать С++/CLI, поэтому я заключил блок кода с вызовами Monitor.Enter() и Monitor.Exit().
Увидел упоминание о List.ForEach выше; 2.0 представил множество операций по набору данных на основе предикатов - Find, FindAll, Exists и т.д. В сочетании с анонимными делегатами вы можете почти добиться простоты 3.5 лямбда-выражений.
", но мои инстинкты говорят мне, что это вырезал бы максимум двух типов бросков операций до максимума один.
Если вы сделаете бросок, как вы предлагали в примере 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)
{
}
Вы можете видеть, что быстрее, консилиумнее и понятнее.
Свойство PreviousPage:
"System.Web.UI.Page, представляющий страницу, передающую управление текущей странице.
Это очень полезно.
Я должен признать, что я не уверен, что это работает лучше или хуже обычного ретранслятора ASP.NET onItemDatabound, но в любом случае здесь мои 5 центов.
MyObject obj = e.Item.DataItem as MyObject;
if(obj != null)
{
//Do work
}
Я думаю, если вам нужно использовать nullable типов, лучше использовать Nullable <.T > а не вопросительный знак нотации. Это делает его очевидно, что магия происходит. Не уверен, почему кто-нибудь захочет использовать Nullable <.bool > хотя.
В веб-службе VB.NET, где параметр не может быть передан (поскольку запрос партнеров не был согласованным или надежным), но ему пришлось пройти проверку на предлагаемый тип (Boolean for "if is search search" ), Отбросьте это до "другого требования руководства"...
... и да, я знаю, что некоторые люди думают, что это не правильный способ сделать это, но IsSearchRequest As Nullable (Of Boolean) спас меня от моего ума в ту ночь!
Отдельные статические поля, зависящие от общего типа окружающего класса.
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>
Когда вам нужно (а) синхронно обмениваться данными между объектами о возникновении события, есть специальный интерфейс 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));
}
Фильтры исключений. Так что "скрытый" вы даже не можете использовать их (по крайней мере, с С#) без патча после компиляции;)
Если вы хотите, чтобы сборщик мусора не запускал финализатор объекта, просто используйте GC.SuppressFinalize(object);
. Аналогичным образом, GC.KeepAlive(object);
не позволит сборщику мусора собирать этот объект, ссылаясь на него. Не очень часто используется, по крайней мере, в моем опыте, но приятно знать на всякий случай.
Не скрыто, но довольно аккуратно. Я нахожу это более сжатой заменой простого 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
Вот совет о том, как вы можете использовать директиву #Region для документирования своего кода.
Некоторые? странность:)
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));
Интересно отметить последний бросок, который по какой-то причине необходим. Ошибка или по дизайну?
Если разрешены сторонние расширения, C5 и Microsoft CCR (см. это сообщение в блоге для быстрого введения) - обязательное знание.
C5 дополняет .Net несколько не хватает библиотеки коллекций (не Set???), а CCR упрощает параллельное программирование (я слышу его из-за объединения с параллельными расширениями).
Я не знаю, является ли это скрытой функцией (""). Любая строковая функция.