2015-04-12 7 views
5

Следующий код создает CA2000« Утилизация объектов перед потерей области ») для первой строки Main, но не второй. Мне бы очень понравилось нарушение CA2000 для второй строки, потому что это (очевидно, упрощенный) шаблон, часто встречающийся в большой базе кода, над которой я работаю.Почему FxCop не сообщает CA2000 для этого тривиального случая неэлементного экземпляра класса?

Кто-нибудь знает, почему нарушение не производится для второй линии?

public static void Main() 
{ 
    new Disposable(); 
    MakeDisposable(); 
} 

private static Disposable MakeDisposable() { return new Disposable(); } 

private sealed class Disposable : IDisposable 
{ 
    public void Dispose() 
    { 
    } 
} 
+0

Попробуйте переименовать свой метод Make создать и посмотреть, если это делает разницу. –

+0

также ... какую версию Visual Studio и/или FxCop вы используете? –

+1

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

ответ

6

Короткий ответ: CA2000 сломан и вряд ли будет исправлен в ближайшее время. См this bug report, который почти точно на ваш вопрос:

Предупреждение CA2000, в частности, это правило, имеет проблемы, и что мы не сможем это исправить в его нынешнем виде.


Чем дольше ответ, что получение CA2000 права является трудно. В прошлых версиях Visual Studio, особенно в 2010 году, ложные предупреждения CA2000 запускались повсюду, и вы ничего не могли сделать, чтобы заставить их уйти. Поиск переполнения стека для любого из десятков вопросов об этом.

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

К сожалению, попытка понять это означает выполнение анализа потока программ скомпилированного ИЛ, чтобы определить, может ли какой-либо путь кода (включая исключительные) пропустить объект. Конечным результатом был почти любой случай, когда вы попытались вернуть IDisposable из метода, приведет к ошибке.

Microsoft ответила на это, делая две вещи:

  • Cranking вниз по чувствительности CA2000, так что это срабатывает только на наиболее очевидные из случаев. Это кажется разумным, так как очевидные случаи, такие как ваша первая строка, скорее всего будут ошибками, чем более неясными, и их легче исправить.
  • Принимает CA2000 из своих рекомендуемых правил анализа кода; обратите внимание, что в их наборе правил «Расширенная правильность» больше нет CA2000.

Наряду с переписыванием компилятора Roslyn появляется возможность для таких инструментов, как FxCop, провести анализ исходного уровня, который может быть проще в этом случае получить. Между тем, общий консенсус, просто выключите CA2000.


В случае, если вы любопытны, немного тестирования, кажется, подтверждает, что текущее правило (2013) CA только пожары если локально содержали, локально построен экземпляр IDisposable капель из области видимости. IOW, объект не может оставить метод, в котором вы его используете new, или CA игнорирует его. Это заставляет меня думать, что CA просто не копает в вызовы методов для его анализа. Помимо попыток заставить замолчать ложные срабатывания, он также может быть частью общей попытки ускорить процесс CA, вырезав некоторые дорогие чеки, которые, по моему мнению, произошли в период с 2010 по 2012 год?

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

class Program 
{ 
    public static void Main() 
    { 
     new Disposable(); // CA2000 

     IDisposable x; 
     MakeDisposable(out x); 

     Test(); 
    } 

    public static Disposable Test() 
    { 
     IDisposable z; 
     var x = MakeDisposable(out z); 
     var y = new Disposable(); // CA2000 
     return x; 
    } 

    private static Disposable MakeDisposable(out IDisposable result) 
    { 
     result = new Disposable(); 

     new Disposable(); // CA2000 
     return new Disposable(); 
    } 
} 
+0

Я думаю, это то, чего я ожидал. Однако моя жалоба не связана с тем фактом, что фабрика не помечена (у нас есть хорошие шаблоны для обеспечения того, чтобы фабрики не протекали), но тот факт, что Main не помечен. Гораздо сложнее обеспечить утечку всех вызывающих абонентов.Наверное, я наивна, но я бы ожидал, что если это правило когда-нибудь поймает что-либо, он сможет поймать обе линии в методе Main. – Beerman006

1

У меня есть догадка, что это происходит из-за сочетания факторов.


1. MakeDisposable прекрасно само по себе:

Там нет ошибки из-за MakeDisposable, потому что, ну, почему бы это? Вы могли бы получить

using (var disposable = MakeDisposable()) 
{ 
    // elided 
} 

в любом месте вашего кода.


2. The ссылки к IDisposable в MakeDisposable не учитывается на Main

Вызов MakeDisposable() в методе Main не вызывает ошибку, потому что компилятор не знает что возвращаемое значение MakeDisposable является только ссылкой на IDisposable.

Другими словами, в глазах компилятора следующие формы MakeDisposable() эквивалентны:

private static Disosable MakeDisposable() 
{ 
    return new Disosable(); 
} 

(оригинал), или подвергая поле подложки, создавая при необходимости:

private static Disposable disposable; 

private static Disposable MakeDisposable() 
{ 
    if (disposable == null) 
     disposable = new Disposable(); 

    return disposable; 
} 

private static void DisposeDisposable() 
{ 
    // elided 
} 

или даже подвергая поле подкладочный уже создали:

private static Disposable disposable = new Disposable(); 

private static Disposable MakeDisposable() 
{ 
    return disposable; 
} 

private static void DisposeDisposable() 
{ 
    // elided 
} 

так т он звонит в Main.

В Main(), где вы инстанцирования IDisposable с new Disposable(); компилятор знает, что вы не передаете его из этой сферы, так правильно выдает ошибку.