2013-12-06 2 views
13

У меня есть фабричный метод, который строит объекты, которые реализуют IDisposable. В конечном счете это вызывающие, которые управляют временем жизни созданных объектов. Эта конструкция вызывает пучок CA2000 errors. Есть ли что-то принципиально неправильное в моем дизайне, нужно ли ему рефакторинг, или это просто слишком волнует предупреждения о статическом анализе кода?CA2000 при возврате одноразового объекта из метода

Завод метод

public static DisposableType BuildTheDisposableType(string param1, int param2) 
{ 
    var theDisposable = new DisposableType(); 

    // Do some work to setup theDisposable 

    return theDisposable 
} 

Вызывающий

using(var dt = FactoryClass.BuildTheDisposableType("data", 4)) 
{ 
    // use dt 
}  
+1

Этот вопрос должен быть прекрасным здесь, но в будущем он может быть лучше подходит для [programers.stackexchange.com] (http://programmers.stackexchange.com/). – gunr2171

+9

@ gunr2171: Я не согласен. Это очень хороший вопрос для [так]. –

+1

Обратите внимание, что если между созданием переменной и оператором return возникает исключение, одноразовое будет протекать. Я бы предложил включить блок 'finally', который проверяет, является ли' theDisposable' ненулевым и вызывает 'theDisposable.Dispose()' если это так. Чтобы вернуть объект, скопируйте ссылку на другую переменную и исключите исходную ссылку. Слишком плохо, нет инструкции «держать», чтобы отменить эффекты «использования» на выбранных путях программы. – supercat

ответ

10

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

Я также рекомендую вам включать в себя обоснование:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", 
    "CA2000:Dispose objects before losing scope", 
    Justification = "This is a factory method. Caller must dispose")] 
+6

У CA2000 нет проблем с методами, возвращающими одноразовые объекты, и вызывающим владельцем. Он пытается сказать нам, что существует путь выполнения, где одноразовый объект не будет передан правильно вызывающему, поэтому ** это не ложное положительное - не подавлять **! @MiklX отправил правильный ответ, я немного очистил его, чтобы уточнить. –

2

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

+0

Передача ответственности - это средство ее управления; проблема заключается в том, что .NET не предоставляет возможности отличать метод, возвращающий ссылку на IDIDposable, принадлежащую кому-либо другому, от того, который передает право собственности на возвращаемый IDisposable' вызывающему, и, следовательно, средства диагностики кода не имеют надежный способ узнать, должны ли они кричать, когда метод возвращает 'IDisposable', не удаляя его, или когда вызывается метод, возвращающий' IDisposable', и его возвращаемое значение остается заброшенным. – supercat

0

Другой альтернативой является изменить фабричный метод в метод «конфигурации» и поставить ответственность за создание одноразового использования объекта на клиенте. Пример:

public void SetupDisosableThing(IDisposable foo) 
{ 
foo.Bar = "baz"; 
} 

void Main() 
{ 
    using (var x = new Thing()) 
    { 
    SetupDisposableThing(x); 
    } 
} 
13

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

public MyDisposable CreateDisposable() 
{ 
    var myDisposable = new MyDisposable(); 
    try 
    { 
     // Additional initialization here which may throw exceptions. 
     ThrowException(); 
    } 
    catch 
    { 
     // If an exception occurred, then this is the last chance to 
     // dispose before the object goes out of scope. 
     myDisposable.Dispose(); 
     throw; 
    } 
    return myDisposable; 
} 

Try, чтобы никогда не оставить одноразовый объект уязвимы для исключения, когда Dispose не будет называться

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

+0

Каков путь выполнения, где он не расположен? –

+0

@JohnSaunders: в ​​исходном вопросе OP, когда исключение вызывается на шаге, обозначенном «// Выполняем некоторые работы по настройкеDisposable». В этот момент был создан одноразовый объект, но все ссылки на него выйдут из области действия, и вызывающий объект получит только исключение (вместо возвращаемого значения), чтобы одноразовый объект оставался висящим. –

+0

Что делать, если 'new MyDisposable()' выдает исключение? Как насчет назначения вызова конструктору в блоке 'try'? Затем выполните «null» тест на 'myDisposable' перед вызовом' myDisposable.Dispose() 'в блоке' catch'. Я нашел этот очищенный CA2000 для меня. – DavidRR