Pregunta

Estoy atascado con la aplicación .Net 1.1 (es decir, no puedo usar los genéricos de 2.0 por ahora), y estaba tratando de optimizar algunas partes del código. Como se trata mucho de envoltorios invocables en tiempo de ejecución, que deben publicarse, terminé creando un método de utilidad que se repite hasta que se liberan todas las referencias. La firma del método es:

void ReleaseObject(object comObject)

Después de lanzar todos los ComObjects, llamo a GC.Collect y GC.WaitForPendingFinalizers (no pregunte, cualquiera que esté relacionado con la interoperabilidad de Office lo sabe).

Y ... como de costumbre, llego a un caso de esquina: si no asigno la referencia administrada correspondiente a nulo antes de la llamada GC.Collect, no se realiza la limpieza correctamente.

Entonces, mi código se ve así:

ReleaseObject(myComObject);
myComObject = null;
GC.Collect()
...

Como, hay un grupo de xxx = null, decidí poner esto en el método util, pero como hay una diferencia entre pasar por referencia y pasar un parámetro de referencia, obviamente tuve que cambiar el método a:

void ReleaseObject(out object comObject)
{
   //do release
   comObject = null;
}

y edite la persona que llama a:

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

Esto falla con un mensaje: " No se puede convertir de 'out MyComClass' a 'out object' "

Si bien puedo pensar por qué puede ser un problema (es decir, la conversión inversa del objeto a MyComClass no está implícita, y no hay garantía de lo que hará el método), me preguntaba si existe una solución alternativa. necesito quedarme con mis cientos de asignaciones de nulos.

Nota: Tengo un montón de diferentes tipos de objetos COM, por eso necesito un " objeto " parámetro, y no un tipo seguro.

¿Fue útil?

Solución

¿Por qué es mejor llamar a un método que simplemente establecer la variable en nulo? Ambas son llamadas de una sola línea, y esta última es mucho más simple.

Sin embargo, suena muy extraño que tengas que establecerlos en nulo en primer lugar. ¿Son estas variables estáticas o variables de instancia cuyos valores deben liberarse antes que su objeto contenedor? Si la variable es solo una variable local que saldrá del ámbito de todos modos, establecerla en nula no debería hacer ninguna diferencia (en la versión).

¿Los RCW no implementan IDisposable? Si es así, llamar a Dispose (preferiblemente a través de una declaración de uso) sería la mejor apuesta.

(Después de las discusiones en los comentarios)

Estas son variables locales, a las que no se hace referencia más adelante en el método. Eso significa que el recolector de basura se dará cuenta de que no necesitan ser tratados como " raíz " referencias, por lo que establecerlas en nulas no debería hacer ninguna diferencia.

Sin embargo, para responder directamente a la pregunta original: no, no puede pasar una variable por referencia a menos que el parámetro del método sea exactamente del mismo tipo, por lo que no tendrá suerte aquí. (Con los genéricos sería posible, pero ha dicho que está limitado a .NET 1.1.)

Otros consejos

Sunny, ref y out son unas sugerencias de clasificación y un contrato para el compilador. La referencia y la salida son un remanente a los días de COM, las sugerencias de ordenación de los objetos cuando se envían por cable / entre procesos.

El contrato out

void foo( out MyClass x)
  1. foo () establecerá x en algo antes de que vuelva.
  2. x no tiene ningún valor cuando se ingresa foo (), y obtienes un error de compilación si intentas usar x antes de configurarlo. (uso del parámetro x no asignado)

El ref contrato

void foo( ref MyClass x)
  1. ref permite cambiar la referencia de las personas que llaman.
  2. x tiene que ser asignable
    • no puedes lanzar algo a una variable intermedia foo (ref (objeto) algo)
    • x no puede ser una propiedad

Es probable que la realidad de los dos últimos puntos le impida hacer lo que está tratando de hacer, porque en efecto no tienen sentido cuando comprende qué son realmente las referencias. Si quieres saberlo, pregúntale a Jon Skeet (él escribió un libro).

Al calcular la referencia, se dice que además del valor de retorno, también se deben devolver los valores de referencia. Cuando se está ordenando, dice que no se moleste en enviar el valor de salida cuando se llama al método, pero recuerde volver a traer el valor de salida además del valor de retorno.


RENUNCIA DE RESPONSABILIDAD RENUNCIA DE RESPONSABILIDAD

Como señalan otros, algo sospechoso está sucediendo. Parece que el código de fuerza bruta que estás manteniendo tiene algunos errores sutiles y sufre de codificación por coincidencia. La mejor solución es probablemente agregar otra capa de direccionamiento indirecto. es decir, una envoltura para la clase de envoltura que garantiza una limpieza determinista donde puede escribir el código desordenado una vez y solo una vez en lugar de salpicarlo a lo largo de su base de código.


Dicho esto ...

Alternativa 1

Ref no hará el truco a menos que proporciones sobrecargas para cada tipo de objeto (com) con el que lo llamarás.

// need a remove method for each type. 
void Remove( ref Com1 x ) { ...; x = null; }
void Remove( ref Con2 x ) { ...; x = null; }
void Remove( ref Com3 x ) { ...; x = null; }

// a generics version using ref.
void RemoveComRef<ComT>(ref ComT t) where ComT : class
{
    System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
    t = null; 
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
Remove( ref c1 );
RemoveComRef(ref c2); // the generics version again.

Alternativa 2

Si no desea hacer eso, devuelva nulo desde el método Remove () y vuelva a convertirlo en el tipo de objeto.

class Remover
{
    // .net 1.1 must cast if assigning
    public static object Remove(object x)
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(x);
        return null;
    }

    // uses generics.
    public static ComT RemoveCom<ComT>(ComT t) where ComT : class
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
        return null;
    }   
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
c1 = (Com1)Remover.Remove(c1); // no reliance on generics
c2 = Remover.RemoveCom(c2); // relies on generics

* Agregué versiones genéricas para comparación.

El código anterior tiene el efecto de que al mirar el código sientes sospechas cuando ves una llamada a Remove (x) sin la asignación (haciendo que el código incorrecto parezca incorrecto). Incluso puedes Grep a través del código base buscando llamadas a Eliminar donde no se realiza la asignación.


RENUNCIA DE RESPONSABILIDAD: todo lo anterior se basa en la necesidad de establecer la referencia en nulo manualmente, lo que (normalmente) no es necesario.

En mi opinión, no podrás configurar esos objetos como nulos en otro método (por cierto, deberías usar el parámetro ref en lugar de out para que sea trabajando, de todos modos, tendría el mismo problema con el error "No se puede convertir ...". Recomiendo crear una matriz de objetos y luego iterar a través de esa matriz, llamando al método ReleaseObject y configurando esos objetos como nulos. Algo así como:

List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
  ReleaseObject(garbagedObject);
  garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...

Debería llamar a Marshal.ReleaseComObject , que AFAIK estaba disponible en 1.1.

Probablemente te refieres a "ref":

static void ReleaseObject(ref object comObject)
{
   if(comObject != null)
   {
     //do release
     comObject = null;
   }
}

[editar re comentarios] sin embargo, esto solo funcionará para objetos sin tipo, por lo que no es muy útil sin genéricos. Oh, para C # 2.0 ...

Re la " ref " ;; Si las variables son realmente variables (es decir, variables del método), entonces quedarán fuera de alcance en breve y se recopilarán. El " ref " solo sería útil para liberar campos. Pero, para ser honesto, sería más simple establecerlos en nulos ...

Un patrón COM típico es:

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top