Другой способ использования Newtonsoft.Json :
dynamic stuff = Newtonsoft.Json.JsonConvert.DeserializeObject("{ color: 'red', value: 5 }");
string color = stuff.color;
int value = stuff.value;
private int myVar;
public int MyVar
{
get { return MyVar; }
}
Blammo. Ваши сбои приложения без отслеживания стека. Происходит все время.
(Замечают капитал MyVar
вместо нижнего регистра myVar
в методе get.)
Я видел, что этот отправил на днях, и я думаю, что это довольно неясно, и болезненно для тех, которые не знают
int x = 0;
x = x++;
return x;
, Когда это возвратится 0 и не 1, как большинство ожидало бы
Если Вы кодируете для MOSS, и Вы добираетесь, сайт ссылаются на этот путь:
SPSite oSiteCollection = SPContext.Current.Site;
и позже в Вашем коде Вы говорите:
oSiteCollection.Dispose();
От MSDN:
при создании объекта SPSite можно использовать Расположить метод для закрытия объекта. Однако, если у Вас есть ссылка на совместно используемый ресурс, такой как тогда, когда объект обеспечивается методом GetContextSite или свойством Site (например, SPContext. Текущий. Сайт), не используйте Расположить метод, чтобы закрыть объект, но вместо этого позволить Windows SharePoint Services или Вашему приложению портала управлять объектом. Для получения дополнительной информации об объектном распоряжении, посмотрите Лучшие практики: Объекты Using Disposable Windows SharePoint Services.
Это происходит с каждым программистом MOSS и некоторой точкой.
Я часто должен напоминать мне, что DateTime является типом значения, не касательно типа. Просто кажется слишком странным мне, особенно рассматривая множество конструкторов для него.
Следующее не поймает исключение в.Net. Вместо этого это приводит к исключению StackOverflow.
private void button1_Click( object sender, EventArgs e ) {
try {
CallMe(234);
} catch (Exception ex) {
label1.Text = ex.Message.ToString();
}
}
private void CallMe( Int32 x ) {
CallMe(x);
}
Для комментаторов (и downvotes):
было бы чрезвычайно редко для переполнения стека быть этим очевидным. Однако, если Вы происходите, Вы не собираетесь ловить исключение и вероятно проведете несколько часов, пытаясь выследить точно, где проблема. Это может быть составлено, если ТАК происходит в небольших используемых логических путях, особенно на веб-приложении, где Вы не могли бы знать точные условия, которые начали проблему.
Это - та же самая ситуация как принятый ответ на этот вопрос ( https://stackoverflow.com/a/241194/2424). Метод get свойства на том ответе по существу делает ту же самую вещь как вышеупомянутый код и отказывает без отслеживания стека.
mystring.Replace("x","y")
, В то время как это похоже, это должно сделать замену на строке, это вызывается на него, на самом деле возвращает новую строку с заменами, сделанными, не меняя струну, на которую это вызывается. Необходимо помнить, что строки неизменны.
use ($contactEmail, $contactName)
". я никогда не знал, как передать переменные закрытию. Спасибо!
– Ryan
25 January 2014 в 19:35
Существует целая книга по Глюки.NET
, Моим фаворитом является тот, где Вы создаете класс в C#, наследовали его к VB и затем пытаетесь повторно наследоваться назад к C#, и он не работает. ARGGH
Тип. GetType
тот, который я видел, кусает много людей, Type.GetType(string)
. Они задаются вопросом, почему это работает на типы в их собственном блоке и некоторые типы как System.String
, но не System.Windows.Forms.Form
. Ответ - то, что это только смотрит в текущем блоке и в mscorlib
.
Анонимные методы
C# 2.0 представил анонимные методы, ведя к противным таким ситуациям:
using System;
using System.Threading;
class Test
{
static void Main()
{
for (int i=0; i < 10; i++)
{
ThreadStart ts = delegate { Console.WriteLine(i); };
new Thread(ts).Start();
}
}
}
, Что это распечатает? Ну, это полностью зависит от планирования. Это распечатает 10 чисел, но это, вероятно, не распечатает 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, который является тем, что Вы могли бы ожидать. Проблема состоит в том, что это i
переменная, которая была получена, не ее значение при создании делегата. Это может быть решено легко с дополнительной локальной переменной правильного объема:
using System;
using System.Threading;
class Test
{
static void Main()
{
for (int i=0; i < 10; i++)
{
int copy = i;
ThreadStart ts = delegate { Console.WriteLine(copy); };
new Thread(ts).Start();
}
}
}
<час> Задержанное выполнение блоков итератора
модульный тест Этого "бедного человека" не передает - почему нет?
using System;
using System.Collections.Generic;
using System.Diagnostics;
class Test
{
static IEnumerable<char> CapitalLetters(string input)
{
if (input == null)
{
throw new ArgumentNullException(input);
}
foreach (char c in input)
{
yield return char.ToUpper(c);
}
}
static void Main()
{
// Test that null input is handled correctly
try
{
CapitalLetters(null);
Console.WriteLine("An exception should have been thrown!");
}
catch (ArgumentNullException)
{
// Expected
}
}
}
ответ - то, что код в источнике эти CapitalLetters
код не становится выполняемым до итератора MoveNext()
, метод сначала называют.
у меня есть некоторые другие причуды на моем страница .
головоломокDictionary<TKey, TValue>
использование EqualityComparer<T>.Default
для сравнений и того компаратора не использует оператор ==
для сравнений. Оператор ==
абсолютно не важен этому вопросу.
– Sam Harwell
10 May 2013 в 00:04
Вот другой тот времени, который получает меня:
static void PrintHowLong(DateTime a, DateTime b)
{
TimeSpan span = a - b;
Console.WriteLine(span.Seconds); // WRONG!
Console.WriteLine(span.TotalSeconds); // RIGHT!
}
<час> TimeSpan. Секунды являются частью секунд промежутка (2 минуты, и 0 секунд имеет значение секунд 0).
TimeSpan. TotalSeconds является всем промежутком, измеренным в секундах (2 минуты имеет общее значение секунд 120).
глюк А, который получает много новых разработчиков, является семантикой исключения переброска.
Партии времени я вижу код как следующий
catch(Exception e)
{
// Do stuff
throw e;
}
, проблема состоит в том, что это вытирает отслеживание стека и делает проблемы диагностирования намного тяжелее, причина, которую Вы не можете отследить где порожденное исключение.
корректный код является любой оператором броска без args:
catch(Exception)
{
throw;
}
Или обертывание исключения в другом и использования внутреннего исключения для получения исходного отслеживания стека:
catch(Exception e)
{
// Do stuff
throw new MySpecialException(e);
}
Если бы Вы считаете ASP.NET, я сказал бы, что жизненный цикл веб-форм является довольно большим глюком мне. Я провел бесчисленные часы, отлаживая плохо записанный код веб-форм, просто потому что много разработчиков просто действительно не понимает, когда использовать, какой обработчик событий (меня включал, печально).
перегруженный == операторы и невведенные контейнеры (arraylists, наборы данных, и т.д.):
string my = "my ";
Debug.Assert(my+"string" == "my string"); //true
var a = new ArrayList();
a.Add(my+"string");
a.Add("my string");
// uses ==(object) instead of ==(string)
Debug.Assert(a[1] == "my string"); // true, due to interning magic
Debug.Assert(a[0] == "my string"); // false
Решения?
всегда использование string.Equals(a, b)
, когда Вы сравниваете строковые типы
дженерики использования как List<string>
, чтобы гарантировать, что оба операнда являются строками.
Когда Вы запускаете процесс (использующий Систему. Диагностика), который пишет в консоль, но Вы никогда не читаете Консоль. Поток, после определенного количества вывода Ваше приложение, будет казаться, зависнет.
struct Point { ... }
List<Point> mypoints = ...;
mypoints[i].x = 10;
не имеют никакого эффекта.
mypoints[i]
возвраты копия Point
объект значения. C# счастливо позволяет Вам изменить поле копии. Тихо выполнение ничего.
Обновление: Это, кажется, фиксируется в C# 3.0:
Cannot modify the return value of 'System.Collections.Generic.List<Foo>.this[int]' because it is not a variable
Сборка "мусора" и Располагает (). Хотя Вы ничего не должны делать к свободному память , все еще необходимо освободить , ресурсы через Располагают (). Это - очень легкая вещь забыть, когда Вы используете WinForms или отслеживаете объекты всегда.
объем переменных циклов foreach!
var l = new List<Func<string>>();
var strings = new[] { "Lorem" , "ipsum", "dolor", "sit", "amet" };
foreach (var s in strings)
{
l.Add(() => s);
}
foreach (var a in l)
Console.WriteLine(a());
печать пять "amet", в то время как следующий пример хорошо работает
var l = new List<Func<string>>();
var strings = new[] { "Lorem" , "ipsum", "dolor", "sit", "amet" };
foreach (var s in strings)
{
var t = s;
l.Add(() => t);
}
foreach (var a in l)
Console.WriteLine(a());
MemoryStream.GetBuffer()
по сравнению с MemoryStream.ToArray()
. Бывшие возвраты целый буфер, последний просто используемая часть. Фу.
Мой худший до сих пор я просто выяснил сегодня... Если Вы переопределяете объект. Равняется (возразите obj), можно волновать обнаружение что:
((MyObject)obj).Equals(this);
не ведет себя то же как:
((MyObject)obj) == this;
Каждый вызовет Вашу переопределенную функцию, другой НЕ будет.
Для программистов C/C++ переход к C# является естественным. Однако самый большой глюк я столкнулся лично (и видели с другими, делающими тот же переход), не полностью понимает различия между классами и структурами в C#.
В C++, классы и структуры идентичны; они только отличаются по видимости по умолчанию, где значение по умолчанию классов к частной видимости и значение по умолчанию структур к общедоступной видимости. В C++ это определение класса
class A
{
public:
int i;
};
функционально эквивалентно этому определению структуры.
struct A
{
int i;
};
В C#, однако, классы являются ссылочными типами, в то время как структуры являются типами значения. Это делает БОЛЬШОЙ различие в (1) решении, когда использовать один по другому, (2) тестировании объектного равенства, (3) производительности (например, упаковывая/распаковывая), и т.д.
существует все виды информации о сети, связанной с различиями между двумя (например, здесь ). Я высоко поощрил бы любого делающего переход к C#, по крайней мере, иметь практическое знание различий и их последствий.
DateTime.ToString ("дд / ММ / гггг") ; На самом деле это будет не всегда давать вам dd / MM / yyyy, а вместо этого будет учитывать региональные настройки и заменять разделитель даты в зависимости от того, где вы находитесь. Так что вы можете получить dd-MM-yyyy или что-то подобное.
Правильный способ сделать это - использовать DateTime.ToString ("dd '/' MM '/' yyyy");
DateTime. ToString ("r") должен преобразовывать в RFC1123, который использует GMT. GMT находится в пределах доли секунды от UTC, но спецификатор формата «r» не преобразуется в UTC , даже если рассматриваемый DateTime указан как Local.
Это приводит к следующему Попался (зависит от того, насколько далеко ваше местное время от UTC):
DateTime.Parse("Tue, 06 Sep 2011 16:35:12 GMT").ToString("r")
> "Tue, 06 Sep 2011 17:35:12 GMT"
Упс!
Сегодня я исправил ошибку, которая долго ускользала. Ошибка была в универсальном классе, который использовался в многопоточном сценарии, а поле static int использовалось для обеспечения синхронизации без блокировки с помощью Interlocked. Ошибка была вызвана тем, что каждый экземпляр универсального класса для типа имеет свой статический код. Таким образом, каждый поток получил свое собственное статическое поле, и он не использовал блокировку, как предполагалось.
class SomeGeneric<T>
{
public static int i = 0;
}
class Test
{
public static void main(string[] args)
{
SomeGeneric<int>.i = 5;
SomeGeneric<string>.i = 10;
Console.WriteLine(SomeGeneric<int>.i);
Console.WriteLine(SomeGeneric<string>.i);
Console.WriteLine(SomeGeneric<int>.i);
}
}
Это печатает 5 10 5
An option would be to have the Abstract class do the calling in this manner. Otherwise, there is no way in c# to require an inherited class to implement a method in a certain way.
public abstract class AbstractClass
{
public void PerformThisFunction()
{
MustBeCalled();
AbstractMethod();
}
public void MustBeCalled()
{
//this must be called when AbstractMethod is invoked
}
//could also be public if desired
protected abstract void AbstractMethod();
}
public class ImplementClass : AbstractClass
{
protected override void AbstractMethod()
{
//when called, base.MustBeCalled() must be called.
//how can i enforce this?
}
}
Doing this creates the desired public facing method in the abstract class, giving the abstract class over how and in what order things are called, while still allowing the concrete class to provide needed functionality.
он не даст вам полного пути:Этот код:
string prefix1 = "C:\\MyFolder\\MySubFolder";
string prefix2 = "C:\\MyFolder\\MySubFolder\\";
string suffix1 = "log\\";
string suffix2 = "\\log\\";
Console.WriteLine(Path.Combine(prefix1, suffix1));
Console.WriteLine(Path.Combine(prefix1, suffix2));
Console.WriteLine(Path.Combine(prefix2, suffix1));
Console.WriteLine(Path.Combine(prefix2, suffix2));
Дает вам следующий результат:
C:\MyFolder\MySubFolder\log\
\log\
C:\MyFolder\MySubFolder\log\
\log\
[Serializable]
class Hello
{
readonly object accountsLock = new object();
}
//Do stuff to deserialize Hello with BinaryFormatter
//and now... accountsLock == null ;)
Мораль истории: инициализаторы полей не запускаются при десериализации объекта
MS SQL Server не может обрабатывать даты до 1753 года. Примечательно, что это не синхронизировано с константой .NET DateTime.MinDate
, которая равна 1 / 1/1. Так что если вы попытаетесь сохранить память, неверную дату (как недавно случилось со мной при импорте данных) или просто дату рождения Вильгельма Завоевателя, у вас будут проблемы. Для этого нет встроенного обходного пути; если вам, вероятно, понадобится работать с датами до 1753 года, вам нужно написать собственный обходной путь.
Утечка памяти из-за того, что вы не отключили события.
Это даже зацепило некоторых старших разработчиков, которых я знаю.
Представьте себе форму WPF с большим количеством вещей в ней, и где-то там вы подписываетесь на событие. Если вы не откажетесь от подписки, то после закрытия и отмены ссылки вся форма будет храниться в памяти.
Я считаю, что проблема, которую я видел, заключалась в создании DispatchTimer в форме WPF и подписке на событие Tick, если вы этого не сделаете. 'Не делайте - = на таймере, ваша форма утекает память!
В этом примере ваш код разрыва должен иметь
timer.Tick -= TimerTickEventHandler;
Это особенно сложно, поскольку вы создали экземпляр DispatchTimer внутри формы WPF, поэтому вы можете подумать что это будет внутренняя ссылка, обрабатываемая процессом сборки мусора ...
Неприятная ошибка кеширования Linq
См. мой вопрос , который привел к этому открытию, и блоггера , обнаружившего проблему.
Короче говоря, DataContext хранит кеш всех объектов Linq-to-Sql, которые вы когда-либо загружали. Если кто-то еще внесет какие-либо изменения в ранее загруженную вами запись, вы не сможете получить последние данные, , даже если вы явно перезагрузите запись!
Это из-за свойства с именем ObjectTrackingEnabled
в DataContext, что по умолчанию истинно. Если вы установите для этого свойства значение false, запись будет загружаться заново каждый раз ... НО ... вы не можете сохранить какие-либо изменения в этой записи с помощью SubmitChanges ().
GOTCHA!
Может быть, это не совсем проблема, потому что поведение ясно написано в MSDN, но однажды сломал мне шею, потому что я нашел его довольно нелогичным:
Image image = System.Drawing.Image.FromFile("nice.pic");
Этот парень уходит из " nice.pic "
файл заблокирован, пока изображение не будет удалено. В то время, когда я столкнулся с этим, я подумал, что было бы неплохо загружать значки на лету, и не осознавал (сначала), что у меня были десятки открытых и заблокированных файлов! Изображение отслеживает, откуда был загружен файл ...
Как решить эту проблему? Я думал, что одинарный лайнер сработает. Я ожидал, что для FromFile ()
появится дополнительный параметр, но его не было, поэтому я написал это ...
using (Stream fs = new FileStream("nice.pic", FileMode.Open, FileAccess.Read))
{
image = System.Drawing.Image.FromStream(fs);
}
В Linq-To-Sql
нет сокращений операторов. См. здесь .
Короче говоря, внутри условного предложения запроса Linq-To-Sql, вы не можете использовать условные ярлыки, такие как ||
и &&
, чтобы избежать исключений с нулевой ссылкой; Linq-To-Sql оценивает обе стороны оператора OR или AND, даже если первое условие избавляет от необходимости оценивать второе условие!
Перечисления можно оценивать более одного раза
Это укусит вас, когда у вас есть ленивый перечисляемый, и вы проводите над ним итерацию дважды и получаете разные результаты. (или вы получаете одни и те же результаты, но выполняете их дважды без необходимости)
Например, во время написания определенного теста мне понадобилось несколько временных файлов для проверки логики:
var files = Enumerable.Range(0, 5)
.Select(i => Path.GetTempFileName());
foreach (var file in files)
File.WriteAllText(file, "HELLO WORLD!");
/* ... many lines of codes later ... */
foreach (var file in files)
File.Delete(file);
Представьте себе мое удивление, когда File.Delete(file)
выбрасывает FileNotFound
! Здесь происходит то, что файлы , перечисленные в списке, получали итерацию дважды (результаты первой итерации просто не запомнились) и на каждой новой итерации вы бы переименовывали
Путь. GetTempFilename()
, так что вы получите другой набор временных имен файлов.
Решение, конечно же, заключается в том, чтобы с нетерпением ожидать получения значения с помощью ToArray()
или ToList()
:
var files = Enumerable.Range(0, 5)
.Select(i => Path.GetTempFileName())
.ToArray();
Это еще страшнее, когда вы делаете что-то многопоточное, например:
foreach (var file in files)
content = content + File.ReadAllText(file);
и вы узнаете content.Length
все равно 0 после всех записей!!!! Затем вы начинаете строго проверять, что у вас нет состояния гонки, когда.... после одного потраченного часа... вы выяснили, что это просто крошечная штучка Enumerable gotcha, которую вы забыли....
Окно наблюдения за Гейзенбергом
Это может сильно вас укусить, если вы выполняете загрузку по требованию, например:
private MyClass _myObj;
public MyClass MyObj {
get {
if (_myObj == null)
_myObj = CreateMyObj(); // some other code to create my object
return _myObj;
}
}
А теперь давайте скажем, у вас есть код в другом месте, использующий это:
// blah
// blah
MyObj.DoStuff(); // Line 3
// blah
Теперь вы хотите отладить свой метод CreateMyObj ()
. Итак, вы ставите точку останова в строке 3 выше, чтобы войти в код. Для удобства вы также помещаете точку останова в строку выше, которая говорит _myObj = CreateMyObj ();
, и даже точку останова внутри самого CreateMyObj ()
.
Код достигает точки останова в строке 3. Вы входите в код. Вы ожидаете ввести условный код, потому что _myObj
, очевидно, имеет значение null, верно? Э ... так ... почему он пропустил условие и сразу перешел к return _myObj
?! Вы наводите указатель мыши на _myObj ...и действительно, это имеет значение! Как это случилось?!
Ответ заключается в том, что ваша среда IDE вызвала получение значения, потому что у вас открыто окно "просмотра" - особенно окно просмотра "Autos", в котором отображаются значения всех переменных / свойств, относящихся к текущей или предыдущей строке. исполнения. Когда вы достигли точки останова в строке 3, окно наблюдения решило, что вам будет интересно узнать значение MyObj
, поэтому за кулисами, игнорируя любую из ваших точек останова , он пошел и вычислил для вас значение MyObj
- , включая вызов CreateMyObj ()
, который устанавливает значение _myObj!
Вот почему я называю это окном наблюдения Гейзенберга - вы не можете наблюдать значение, не влияя на него ... :)
GOTCHA!
Изменить - я считаю, что комментарий @ ChristianHayter заслуживает включения в основной ответ, потому что он выглядит как эффективное решение этой проблемы. Поэтому каждый раз, когда у вас есть свойство с отложенной загрузкой ...
Украсьте свое свойство [DebuggerBrowsable (DebuggerBrowsableState.Never)] или [DebuggerDisplay ("<загружено по запросу>")]. - Кристиан Хейтер