2010-06-27 6 views
8

Tengo un montón de código como este:C# CA2000: Eliminar objetos antes de perder el alcance usando FileStream/XMLTextReader

FileStream fs = File.Open(@"C:\Temp\SNB-RSS.xml", FileMode.Open); 
using (XmlTextReader reader = new XmlTextReader(fs)) 
{ 
    /* Some other code */ 
} 

Esto me da la siguiente advertencia de análisis de código:

CA2000 : Microsoft.Reliability : In method 'SF_Tester.Run()', object 'fs' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'fs' before all references to it are out of scope. 

Si sigo el sugerencia y pongo el File.Open en una instrucción using, me sale esto:

CA2202 : Microsoft.Usage : Object 'fs' can be disposed more than once in method 'SF_Tester.Run()'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 39 

estoy usando VS2010 y No puedo evitar pensar que estoy haciendo algo mal, pero no lo veo. ¿Qué estoy haciendo mal?

+0

FYI, "new XmlTextReader" ha quedado en desuso desde .NET 2.0. Use 'XmlReader.Create' en su lugar, como Hans le muestra a continuación. –

+0

Jeepers ... No sabía que XmlTextReader había quedado en desuso. Esto tiene mucho más sentido ahora. ¡Gracias! –

+0

El motivo de 'CA2202' es que la eliminación de un' Reader' o 'Writer' también elimina el' FileStream' utilizado para crearlo; esto significa que tiene que deshacerse de 'fs' si la construcción del' Writer' falla, pero no si tiene éxito; eso lleva a [la respuesta a continuación] (http://stackoverflow.com/a/3700444/562906) por ** testalino **. – PJTraill

Respuesta

15

Suspiro, agotando no es eso. Evite todo esto usando el método Create() recomendado:

using (var reader = XmlReader.Create(@"C:\Temp\SNB-RSS.xml")) { 
    //... 
} 
1

Solo estoy adivinando; no tiene tiempo para pasar por un análisis completo ahora.

Supongamos que el constructor XmlTextReader 'toma posesión' de la secuencia que se transmite, por lo que disponer XmlTextReader también dará como resultado la secuencia subyacente. Eso explicaría el comportamiento que ves. Quizás el constructor XmlTextReader pueda lanzar, y en ese caso, la advertencia original sobre fs tendría sentido. Sin embargo, dada esa hipótesis, este código

 var fs = File.Open(@"C:\Temp\SNB-RSS.xml", FileMode.Open); 
     XmlTextReader reader = null; 
     try 
     { 
      reader = new XmlTextReader(fs); 
     } 
     finally 
     { 
      if (reader== null) 
      { 
       fs.Dispose(); 
      } 
     } 
     if (reader != null) 
     { 
      using (reader) 
      { 
       /* Some other code */ 
      } 
     } 

es, creo, correcta, pero aún así los rendimientos de una advertencia falsa. Esto huele como un buen ejemplo que demuestra las limitaciones de las herramientas de análisis estático.

Como dijo otra persona, hay otra API para crear directamente el lector a partir del nombre de archivo (XmlReader.Create()), que evita todo esto (y muestra cómo las API bien diseñadas enfocadas en escenarios son una buena razón para una sorprendente variedad de razones)

1

Es un problema conocido

http://connect.microsoft.com/VisualStudio/feedback/details/535118/ca2000-and-ca2202-offer-contradictory-warnings

Si estás utilizando un StreamWriter en lugar de XMLTextReader (como en la solución anterior) se puede utilizar un método similar mediante el constructor correspondiente; p.ej.

var sw = new StreamWriter("filename.txt"); 

o

var sw = new StreamWriter("filename.txt", /*append to file = */ false); 

No se desprende de la documentación si la primera forma de constructor será sobrescribir o anexar a un archivo.

11

Como nadie proporciona una solución que resuelve este problema, sin embargo, estoy escribiendo mi solución de trabajo aquí abajo:

FileStream fs = new FileStream(fileName, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite); 
try 
{ 
    using (var fileWriter = new StreamWriter(fs, encoding)) 
    { 
     fs = null; 
     fileWriter.Write(content); 
    } 
} 
finally 
{ 
    if (fs != null) 
     fs.Dispose(); 
} 

Esto elimina CA2000.

+0

Ok, he estado buscando toda la mañana y este es el primer enfoque que realmente funcionó. ¡Gracias! Mi problema era con StreamWriter, no con XMLReader, sino con el mismo CA Hell. – TheZenker

+0

En realidad, esta es la mejor respuesta. Vea http://connect.microsoft.com/VisualStudio/feedback/details/611525/disposable-objects-and-ca2000-ca2202-warnings – ken2k

+0

** testalino **: Puede desear integrar esto en su respuesta: El motivo que 'usar fs' como en la pregunta causa' CA2202' es que deshacerse de un 'Reader' o' Writer' también elimina el 'FileStream' usado para crearlo; esto significa que tiene que deshacerse de 'fs' si la construcción del' Writer' falla, pero no si tiene éxito. Esto me parece un defecto en el diseño de estas clases. No puedo decir que me guste tener que usar tu patrón, pero no veo un mejor enfoque; escribir clases de envoltura parece equivocado. – PJTraill

0

Como se menciona en this respuesta, la única forma de evitarlo es hacer as recommended in CA2202 y usar un bloque externo try-finally en lugar de un bloque externo. Dentro del uso interno, establezca el objeto IDisposable externo en nulo para evitar que se acceda una vez que el uso interno haya finalizado.

Aquí es un envoltorio genérico que hace "correctamente", es decir, trabaja en todo el XmlReader mal diseñado (propiedad tal vez no debería haber tomado de la corriente que recibe? No está seguro de cuál es la forma correcta de hacerlo sería)

de exención de responsabilidad: En realidad no probado

public static TResult SafeNestedUsing<TOuter, TInner, TResult>(Func<TOuter> createOuterDisposable, Func<TOuter, TInner> createInnerDisposable, Func<TInner, TResult> body) 
     where TInner : IDisposable 
     where TOuter : class, IDisposable 
    { 
     TOuter outer = null; 
     try 
     { 
      outer = createOuterDisposable(); 
      using (var inner = createInnerDisposable(outer)) 
      { 
       var result = body(inner); 
       outer = null; 
       return result; 
      } 
     } 
     finally 
     { 
      if (null != outer) 
      { 
       outer.Dispose(); 
      } 
     } 
    } 

Ejemplo de uso:

SafeNestedUsing<MemoryStream, XmlReader, XmlDocument>(
    ()   => new MemoryStream(array), 
    (memStream) => XmlReader.Create(memStream, xmlReaderSettings), 
    (xmlReader) => 
    { 
     XmlDocument xmlDoc = new XmlDocument(); 
     xmlDoc.Load(xmlReader); 
     return xmlDoc; 
    }); 

Este es bastante torpe, y puede argumentar que es mejor repetir el patrón try/set null/finally en su lugar. Pero para un patrón repetitivo de usos anidados, preferiría hacerlo de esta manera que repetir todo el proceso cada vez.

0

sólo tiene que utilizar 'utilizar' para el filestream

using(FileStream fs = new FileStream(fileName, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite)) 
{ 
// some codes here 

} 

No modifique fs y no utilice fs.close() en el interior se utilizan llaves.

Cuestiones relacionadas