Utilisation de & # 8220; réf & # 8221; et / ou & # 8220; out & # 8221; pour type d'objet

StackOverflow https://stackoverflow.com/questions/244826

  •  05-07-2019
  •  | 
  •  

Question

Je suis coincé avec l’application .Net 1.1 (c’est-à-dire que je ne peux pas utiliser les bonus génériques de la version 2.0 pour le moment), et j’essayais d’optimiser certaines parties du code. Comme il traite beaucoup de wrappers appelables à l'exécution, qui doivent être libérés, j'ai fini par créer une méthode utilitaire qui boucle jusqu'à ce que toutes les références soient libérées. La signature de la méthode est:

void ReleaseObject(object comObject)

Après avoir publié tous les comObjects, j’appelle GC.Collect et GC.WaitForPendingFinalizers (ne le demandez pas: quiconque s’occupant d’Office interop le sait).

Et ... comme d'habitude, j'arrive à un point critique: si je n'attribue pas la référence gérée correspondante à null avant l'appel GC.Collect, le nettoyage n'est pas correct.

Donc, mon code ressemble à:

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

Comme il y a beaucoup de xxx = null, j'ai décidé de mettre cela dans la méthode util, mais comme il y a une différence entre passer par référence et transmettre un paramètre de référence, j'ai évidemment dû changer la méthode en:

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

et éditez l'appelant en:

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

Cela échoue avec le message suivant: "Impossible de convertir" out MyComClass "en" out object ""

Bien que je puisse comprendre pourquoi cela peut être un problème (c'est-à-dire que la conversion inverse d'objet en MyComClass n'est pas implicite et que rien ne garantit ce que la méthode va faire), je me demandais s'il y avait une solution de contournement, ou besoin de rester avec mes centaines d'assignations de NULL.

Remarque: j'ai plusieurs types d'objets COM, c'est pourquoi j'ai besoin d'un "objet". paramètre, et non de type sûr.

Était-ce utile?

La solution

Pourquoi est-il préférable d'appeler une méthode plutôt que de définir la variable sur null? Ce sont tous les deux des appels sur une seule ligne, et le dernier est beaucoup plus simple.

Cependant, cela semble très étrange que vous ayez à les définir comme nuls. S'agit-il de variables statiques ou de variables d'instance dont les valeurs doivent être publiées plus tôt que leur objet contenant? Si la variable est juste une variable locale qui sortira de son champ de toute façon, la définir sur null ne fera aucune différence (dans la version).

Les RCW n’implémentent-ils pas IDisposable? Dans ce cas, appeler Dispose (de préférence via une instruction using) serait le meilleur choix.

(Après discussions dans les commentaires.)

Ce sont des variables locales, qui ne sont pas référencées plus tard dans la méthode. Cela signifie que le ramasse-miettes va se rendre compte qu'il n'est pas nécessaire de les traiter comme "racine". références - les définir sur null ne devrait donc faire aucune différence.

Cependant, pour répondre directement à la question initiale: non, vous ne pouvez pas transmettre une variable par référence à moins que le paramètre de méthode ne soit exactement du même type. Vous n'avez donc pas de chance. (Avec les génériques, ce serait possible, mais vous avez dit que vous étiez limité à .NET 1.1.)

Autres conseils

Sunny, ref et out sont des astuces de mise en forme + un contrat avec le compilateur. Ref et out sont un report vers COM jours - les astuces de classement pour les objets lorsqu’ils sont envoyés sur le réseau / entre processus.

Le contrat out

void foo( out MyClass x)
  1. foo () définira x sur quelque chose avant son retour.
  2. x n'a aucune valeur lorsque foo () est entré et vous obtenez une erreur du compilateur si vous essayez d'utiliser x avant de le définir. (utilisation du paramètre x non affecté)

Le contrat ref

void foo( ref MyClass x)
  1. ref permet de changer la référence de l'appelant.
  2. x doit être assignable
    • vous ne pouvez pas convertir quelque chose en une variable intermédiaire foo (ref (object) quelque chose)
    • x ne peut pas être une propriété

La réalité des deux derniers points vous empêchera probablement de faire ce que vous essayez de faire, car, en réalité, ils n’ont aucun sens lorsque vous comprenez ce que sont réellement les références. Si vous voulez savoir cela, demandez à Jon Skeet (il a écrit un livre).

Lors du marshalling ref, il est indiqué que, en plus de la valeur de retour, renvoie également les valeurs de référence. Lors du triage, vous ne devez pas vous soucier d’envoyer la valeur de sortie lorsque la méthode est appelée, mais rappelez-vous de ramener la valeur de sortie en plus de la valeur de retour.

AVERTISSEMENT AVERTISSEMENT AVERTISSEMENT

Comme d'autres l'ont fait remarquer, il se passe quelque chose de louche. Il semble que le code de force brute que vous maintenez présente des bugs subtils et souffre de codage par coïncidence. La meilleure solution consiste probablement à ajouter une autre couche d'indirection. c.-à-d. un wrapper à la classe wrapper qui assure un nettoyage déterministe dans lequel vous pouvez écrire le code en désordre une et une seule fois au lieu de le modifier dans votre base de code.

Cela dit ..

Alternative 1

Ref ne fera pas l'affaire à moins de fournir des surcharges pour chaque type d'objet (com) avec lequel vous l'appellerez.

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

Alternative 2

Si vous ne voulez pas faire cela, renvoyez null à partir de la méthode Remove () et retournez le type d'objet.

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

* J'ai ajouté des versions génériques à des fins de comparaison.

Le code ci-dessus a pour effet que, lorsque vous examinez le code, vous devenez suspect lorsque vous voyez un appel à Remove (x) sans l'attribution (donner à un code erroné une apparence incorrecte). Vous pouvez même passer par la base de code pour rechercher des appels à supprimer où l’attribution n’a pas lieu.

CLAUSE DE NON-RESPONSABILITÉ: tout ce qui est mentionné ci-dessus dépend du fait que vous devez définir manuellement la référence à null, ce qui n'est (normalement) pas nécessaire.

À mon avis, vous ne pourrez pas définir la valeur null de ces objets dans une autre méthode (vous devez utiliser le paramètre ref au lieu de out ). de toute façon, vous rencontriez le même problème avec l'erreur "Impossible de convertir ...".) Je recommanderais de créer un tableau d'objets et d'itérer ce tableau en appelant la méthode ReleaseObject et en définissant ces objets sur null. Quelque chose comme:

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

Vous devriez appeler Marshal.ReleaseComObject , pour lequel AFAIK était disponible dans 1.1.

Vous voulez probablement dire "ref":

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

[modifier les commentaires] cependant, cela ne fonctionnera que pour les objets non typés, donc pas d'utilisation sans génériques! Oh, pour C # 2.0 ...

Re le "ref" ;; si les variables sont vraiment des variables (ce qui signifie: variables de méthode), elles sortiront bientôt de leur champ d’application et seront collectées. Le " ref " ne serait utile que pour libérer les champs. Mais pour être honnête, il serait plus simple de leur donner la valeur null ...

Un modèle COM typique est:

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top