Pergunta

Estou preso ao aplicativo .Net 1.1 (ou seja,Não posso usar os itens genéricos do 2.0 por enquanto) e estava tentando otimizar algumas partes do código.Como se trata muito de wrappers que podem ser chamados em tempo de execução, que precisam ser liberados, acabei criando um método utilitário que faz loop até que todas as referências sejam liberadas.A assinatura do método é:

void ReleaseObject(object comObject)

Depois de liberar todos os comObjects, chamo GC.Collect e GC.WaitForPendingFinalizers (não pergunte - qualquer pessoa que lide com a interoperabilidade do Office sabe).

E ...como de costume, encontrei um caso estranho - se eu não atribuir a referência gerenciada correspondente a null antes da chamada GC.Collect, ela não será limpa corretamente.

Então, meu código se parece com:

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

Como tem um monte de xxx=null, resolvi colocar isso no método util, mas como existe uma diferença entre passar por referência, e passar um parâmetro de referência, obviamente tive que mudar o método para:

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

e edite o chamador para:

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

Isso falha com uma mensagem:"Não é possível converter de 'out MyComClass' para 'out object'"

Embora eu possa pensar por que isso pode ser um problema (ou seja,a conversão reversa do objeto para MyComClass não está implícita e não há garantia do que o método fará), eu queria saber se há uma solução alternativa ou preciso ficar com minhas centenas de atribuições de nulos.

Observação:Eu tenho vários tipos de objetos COM diferentes, é por isso que preciso de um parâmetro "objeto" e não de um tipo seguro.

Foi útil?

Solução

Por que é melhor chamar um método do que apenas definir a variável como nula?Ambas são chamadas de linha única e a última é muito mais simples.

Parece muito estranho que você precise defini-los como nulos em primeiro lugar.Essas são variáveis ​​estáticas ou variáveis ​​de instância cujos valores precisam ser liberados antes do objeto que as contém?Se a variável for apenas uma variável local que sairá do escopo de qualquer maneira, defini-la como nula não deverá fazer nenhuma diferença (no lançamento).

Os RCWs não implementam IDisposable?Nesse caso, chamar Dispose (de preferência por meio de uma instrução using) seria a melhor aposta.

(Após discussões nos comentários.)

Estas são variáveis ​​locais, que não são referenciadas posteriormente no método.Isso significa que o coletor de lixo perceberá que eles não precisam ser tratados como referências "raiz" - portanto, defini-los como nulos não deve fazer nenhuma diferença.

Para responder diretamente à pergunta original, no entanto:não, você não pode passar uma variável por referência, a menos que o parâmetro do método seja exatamente do mesmo tipo, então você está sem sorte aqui.(Com genéricos seria possível, mas você disse que está limitado ao .NET 1.1.)

Outras dicas

Sunny, ref e out são dicas de triagem + contrato para o compilador.Ref e out são uma herança dos dias COM - as dicas de triagem para objetos quando enviados pela rede/entre processos.

O out contrato

void foo( out MyClass x)
  1. foo() irá definir x para algo antes de retornar.
  2. x não tem valor quando foo() é inserido e você receberá um erro do compilador se tentar usar x antes de configurá-lo.(uso do parâmetro x não atribuído)

O ref contrato

void foo( ref MyClass x)
  1. ref permite alterar a referência do chamador.
  2. x tem que ser atribuível
    • você não pode converter algo em uma variável intermediária foo( ref (objeto) alguma coisa)
    • x não pode ser uma propriedade

A realidade dos dois últimos pontos provavelmente o impedirá de fazer o que está tentando fazer, porque na verdade eles não fazem sentido quando você entende o que realmente são as referências.Se você quiser saber isso, pergunte a Jon Skeet (ele escreveu um livro).

Ao empacotar ref, diz que além do valor de retorno, traga de volta os valores de ref também.Ao fazer o empacotamento, diz não se preocupe em enviar o valor de saída quando o método for chamado, mas lembre-se de trazer de volta o valor de saída além do valor de retorno.


AVISO LEGAL AVISO LEGAL

Como outros apontam, algo suspeito está acontecendo.Parece que o código de força bruta que você está mantendo tem alguns bugs sutis e sofre codificação por coincidência.A melhor solução é provavelmente adicionar outra camada de indireção.ou sejaUm wrapper para a classe wrapper que garante uma limpeza determinística onde você pode escrever o código confuso uma vez e apenas uma vez, em vez de espalhá-lo por toda a sua base de código.


Dito isto ..

Alternativa 1

Ref não resolverá o problema, a menos que você forneça sobrecargas para cada tipo de objeto (com) com o qual você o chamará.

// 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

Se você não quiser fazer isso, retorne null do método Remove() e converta de volta para o 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

* Adicionei versões genéricas para comparação.

O código acima tem o efeito de que, ao olhar para o código, você fica desconfiado ao ver uma chamada para Remove(x) sem a atribuição (fazendo com que o código errado pareça errado).Você pode até percorrer a base de código procurando chamadas para Remove onde a atribuição não ocorre.


AVISO LEGAL - tudo o que foi dito acima se baseia na necessidade de definir a referência como nula manualmente, o que (normalmente) não é necessário.

Na minha opinião, você não será capaz de definir esses objetos como nulos em outro método (aliás, você precisaria usar referência parâmetro em vez de fora Para fazê -lo funcionar, de qualquer maneira você atingiria o mesmo problema com o erro "não pode converter ...".) Eu recomendaria criar e matar objetos e depois iterar através dessa matriz, chamando o método do RELEJETOBJET e definindo esses objetos para NULL .Algo como:

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

Você deveria estar ligando Marshal.ReleaseComObject, que AFAIK estava disponível em 1.1.

Você provavelmente quer dizer "ref":

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

[editar comentários] no entanto, isso só funcionará para objetos não digitados, portanto, não há muita utilidade sem genéricos!Ah, para C # 2.0 ...

Re o "ref";se as variáveis ​​são verdadeiramente variáveis ​​(ou seja:variáveis ​​de método), então elas sairão do escopo em breve e serão coletadas.O "ref" só serviria para liberar campos.Mas para ser honesto, seria mais simples defini-los como nulos...

Um padrão COM típico é:

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top