Почему «используется» улучшение производительности C #

== проверяет ссылки на объекты, .equals() проверяет строковые значения.

Иногда кажется, что == сравнивает значения, потому что Java делает некоторые закулисные вещи, чтобы убедиться, что одинаковые строки в строке являются одним и тем же объектом.

Для Например:

String fooString1 = new String("foo");
String fooString2 = new String("foo");

// Evaluates to false
fooString1 == fooString2;

// Evaluates to true
fooString1.equals(fooString2);

// Evaluates to true, because Java uses the same object
"bar" == "bar";

Но будьте осторожны с нулями!

== обрабатывает строки null в порядке, но вызов .equals() из пустой строки приведет к исключению:

String nullString1 = null;
String nullString2 = null;

// Evaluates to true
System.out.print(nullString1 == nullString2);

// Throws a NullPointerException
System.out.print(nullString1.equals(nullString2));

Итак, если вы знаете, что fooString1 может но не менее очевидно, что он проверяет значение null (из Java 7):

System.out.print(Objects.equals(fooString1, "bar"));
13
задан Wernight 17 June 2010 в 15:13
поделиться

11 ответов

Пара моментов:

Вызов Dispose не увеличивает производительность. IDisposable предназначен для сценариев, в которых вы используете ограниченные и/или неуправляемые ресурсы, которые не могут быть учтены временем выполнения.

Не существует четкого и очевидного механизма, как компилятор мог бы обращаться с объектами IDisposable в коде. Что делает его кандидатом на автоматическую утилизацию, а что нет? Если экземпляр является (или может быть) раскрыт вне метода? Ничто не говорит о том, что только потому, что я передаю объект другой функции или классу, я хочу, чтобы его можно было использовать вне рамок метода

Рассмотрим, например, фабричный паттерн, который принимает Stream и десериализует экземпляр класса.

public class Foo
{
    public static Foo FromStream(System.IO.Stream stream) { ... }
}

И я вызываю его:

Stream stream = new FileStream(path);

Foo foo = Foo.FromStream(stream);

Теперь я могу хотеть или не хотеть, чтобы этот Stream утилизировался при выходе из метода. Если фабрика Foo считывает все необходимые данные из Stream и больше не нуждается в них, то я хочу, чтобы они были утилизированы. Если же объект Foo должен удерживать поток и использовать его в течение своей жизни, то я бы не хотел, чтобы он был утилизирован.

Аналогично, как насчет экземпляров, получаемых не из конструктора, а из чего-то другого, например Control.CreateGraphics(). Эти экземпляры могут существовать вне кода, поэтому компилятор не будет избавляться от них автоматически.

Предоставление пользователю контроля (и обеспечение такой идиомы, как блок using) делает намерение пользователя ясным и облегчает обнаружение мест, где экземпляры IDisposable не утилизируются должным образом. Если бы компилятор автоматически утилизировал некоторые экземпляры, то отладка была бы намного сложнее, поскольку разработчику пришлось бы расшифровывать, как правила автоматического удаления применяются к каждому блоку кода, использующему объект IDisposable.

В конце концов, есть две причины (по соглашению) для реализации IDisposable в типе.

  1. Вы используете неуправляемый ресурс (то есть вы делаете вызов P/Invoke, который возвращает что-то вроде хэндла, который должен быть освобожден другим вызовом P/Invoke)
  2. Ваш тип имеет экземпляры IDisposable, которые должны быть утилизированы, когда время жизни этого объекта закончится.

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

21
ответ дан 1 December 2019 в 17:45
поделиться

Думаю, вы думаете о финализаторах. Финализаторы используют синтаксис деструктора в C # и автоматически вызываются сборщиком мусора. Финализаторы подходят только для очистки неуправляемых ресурсов.

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

Обнаружение на самом деле сложнее, чем кажется. Что, если у вас есть такой код:


  var mydisposable = new...
  AMethod(mydisposable);
  // (not used again)

Возможно, какой-то код в AMethod сохраняет ссылку на myDisposable.

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

  • Может быть, myDisposable подписывается на событие внутри AMethod (тогда издатель события содержит ссылку на myDisposable)

  • Может быть, AMethod порождает другой поток

  • Возможно, mydisposable становится «заключенным» анонимным методом или выражением lamba внутри AMethod.

Все это затрудняет абсолютную уверенность в том, что ваш объект больше не используется, поэтому Dispose позволяет разработчику сказать: «Хорошо, я знаю, что теперь запускать мой код очистки безопасно);

Имейте в виду, что dispose не освобождает объект - это может сделать только сборщик мусора.(У GC действительно есть волшебство, чтобы понять все сценарии, которые я описал, и он знает, когда очищать ваш объект, и если вам действительно нужен код для запуска, когда GC не обнаруживает ссылок, вы можете использовать финализатор). Однако будьте осторожны с финализаторами - они предназначены только для неуправляемых выделений, которыми владеет ваш класс.

Подробнее об этом читайте здесь: http://msdn.microsoft.com/en-us/magazine/bb985010.aspx и здесь: http://www.bluebytesoftware.com/blog/2005/04/08/DGUpdateDisposeFinalizationAndResourceManagement.aspx

Если вам нужна неуправляемая очистка дескрипторов, прочтите также о SafeHandles.

2
ответ дан 1 December 2019 в 17:45
поделиться

Сборка мусора (хотя и не имеет прямого отношения к IDisposable, это то, что очищает неиспользуемые объекты) не так проста.

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

Если бы у вас был следующий код:

public void DoSomeWork(SqlCommand command)
{
    SqlConnection conn = new SqlConnection(connString);

    conn.Open();

    command.Connection = conn;

    // Rest of the work here
 }

Как бы компилятор узнал, когда вы закончили использовать объект conn? Или если вы передали ссылку какому-то другому методу, который удерживал его?

Явный вызов Dispose() или использование блока using четко указывает на ваше намерение и заставляет все очистить должным образом.

Теперь вернемся к производительности. Простой вызов Dispose() на объекте не гарантирует увеличения производительности. Метод Dispose() используется для "очистки" ресурсов, когда вы закончили работу с объектом.

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

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

16
ответ дан 1 December 2019 в 17:45
поделиться

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

Вот почему у нас есть сборка мусора. Проблема со сборкой мусора в том, что она выполняется с неопределенным интервалом, и обычно, если объект реализует IDisposable, причина в том, что вы хотите иметь возможность избавиться от него немедленно. Например, прямо сейчас немедленно. Такие конструкции, как соединения баз данных, являются одноразовыми не только потому, что у них есть какая-то особая работа, которую нужно выполнить, когда их выбрасывают - это также потому, что они дефицитны.

1
ответ дан 1 December 2019 в 17:45
поделиться

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

Неустойчивые объекты также могут вызывать беспокойство.

Кроме того, с помощью () {....} более читабелен и интуитивно понятен, что многого стоит с точки зрения ремонтопригодности.

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

3
ответ дан 1 December 2019 в 17:45
поделиться

C++ поддерживает это; они называют это "семантикой стека для ссылочных типов". Я поддерживаю добавление этого в C#, но это потребует другого синтаксиса (изменение семантики в зависимости от того, передается ли локальная переменная другому методу или нет, не является хорошей идеей).

2
ответ дан 1 December 2019 в 17:45
поделиться

Посмотрите на MSDN Artilce для C# Using Statement Оператор using - это просто сокращение, чтобы не делать try и finally во многих местах. Вызов dispose не является функцией низкого уровня, как сборка мусора.

Как вы можете видеть, using переводится как.

{
  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }
}

Как компилятор узнает, куда поместить блок finally? Вызывает ли он его при Garbage Collection?

Garabage Collection не происходит, как только вы покидаете метод. Прочитайте эту статью о Garbage Collection, чтобы лучше понять ее. Только после того, как на объект не будет ссылок. Ресурс может быть связан гораздо дольше, чем нужно.

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

3
ответ дан 1 December 2019 в 17:45
поделиться

Мне кажется, что G.C. трудно понять, что вы больше не будете использовать эту переменную позже в том же методе. Очевидно, что если вы покинете метод и не сохраните дальнейших ссылок на переменную, то G.C. избавится от нее. Но использование using в вашей выборке говорит G.C., что вы уверены что больше не будете использовать эту переменную после.

0
ответ дан 1 December 2019 в 17:45
поделиться

Оператор using не имеет ничего общего с производительностью (если только вы не рассматриваете предотвращение утечек ресурсов / памяти как производительность).

Все, что он делает для вас, - это гарантия того, что метод IDisposable.Dispose вызывается для рассматриваемого объекта, когда он выходит за пределы области видимости, даже если внутри блока using произошло исключение.

Затем метод Dispose () отвечает за освобождение всех ресурсов, используемых объектом. Чаще всего это неуправляемые ресурсы, такие как файлы, шрифты, изображения и т. Д., Но они также могут быть простыми действиями по «очистке» управляемых объектов (но не сборкой мусора).

Конечно, если метод Dispose () реализован плохо, оператор using не дает никаких преимуществ.

0
ответ дан 1 December 2019 в 17:45
поделиться

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

Я думаю, что OP говорит, что

public void SomeMethod() 
{ 
    ... 

    var foo = new Foo();

    ... do stuff with Foo ... 


    // Foo isn't use after here (obviously). 
    ... 
} 

должно быть эквивалентно

public void SomeMethod() 
{ 
    ... 

    using (var foo = new Foo())
    {
    ... do stuff with Foo ... 
    }

    // Foo isn't use after here (obviously). 
    ... 
} 

, потому что Foo больше не используется.

Ответ, конечно же, заключается в том, что компилятор не может решить это довольно легко. Сборка мусора (которая волшебным образом называется Dispose () в .NET) - очень сложное поле. Тот факт, что символ ниже не используется, не означает, что переменная не используется.

Возьмем этот пример:

public void SomeMethod() 
{ 
    ... 

    var foo = new Foo();
    foo.DoStuffWith(someRandomObject);
    someOtherClass.Method(foo);

    // Foo isn't use after here (obviously).
    // Or is it?? 
    ... 
} 

В этом примере someRandomObject и someOtherClass могут иметь ссылки на то, что указывает Foo, поэтому, если мы вызовем Foo.Dispose (), они сломаются. Вы говорите, что просто представляете простой случай, но единственный «простой случай», когда то, что вы предлагаете, работает, - это случай, когда вы не вызываете методы из Foo и не передаете Foo или любой из его членов чему-либо еще - эффективно, когда вы вообще не используете Foo, и в этом случае вам, вероятно, не нужно его объявлять. Даже в этом случае вы никогда не можете быть уверены, что какое-то отражение или взлом событий не получило ссылку на Foo только при его создании или что Foo не подключился к чему-то еще во время своего конструктора.

0
ответ дан 1 December 2019 в 17:45
поделиться

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

0
ответ дан 1 December 2019 в 17:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: