CA1021 : Éviter les paramètres outCA1021: Avoid out parameters

TypeNameTypeName AvoidOutParametersAvoidOutParameters
CheckIdCheckId CA1021CA1021
CategoryCategory Microsoft.DesignMicrosoft.Design
Modification avec ruptureBreaking Change RuptureBreaking

CauseCause

Une méthode publique ou protégée dans un type public a un out paramètre.A public or protected method in a public type has an out parameter.

Description de la règleRule Description

Passer des types par référence (à l’aide de out ou ref) nécessite une certaine expérience des pointeurs, de comprendre la différence entre les types valeur et les types référence et la gestion de méthodes impliquant plusieurs valeurs de retour.Passing types by reference (using out or ref) requires experience with pointers, understanding how value types and reference types differ, and handling methods with multiple return values. En outre, la différence entre out et ref paramètres est généralement peu comprise.Also, the difference between out and ref parameters is not widely understood.

Lorsqu’un type référence est passé de « par référence », la méthode prévoit d’utiliser le paramètre pour retourner une instance différente de l’objet.When a reference type is passed "by reference," the method intends to use the parameter to return a different instance of the object. Passez un type référence par référence est également appelé à l’aide du pointeur, pointeur vers un pointeur, ou une double indirection double.Passing a reference type by reference is also known as using a double pointer, pointer to a pointer, or double indirection. À l’aide de la valeur par défaut de convention d’appel, qui est de passer « par valeur », un paramètre qui accepte un type référence déjà reçoit un pointeur vers l’objet.By using the default calling convention, which is pass "by value," a parameter that takes a reference type already receives a pointer to the object. Le pointeur, pas l’objet sur lequel il pointe, est passé par valeur.The pointer, not the object to which it points, is passed by value. Passer par valeur signifie que la méthode ne peut pas modifier le pointeur afin qu’il pointe vers une nouvelle instance du type référence.Pass by value means that the method cannot change the pointer to have it point to a new instance of the reference type. Toutefois, il peut modifier le contenu de l’objet sur lequel il pointe.However, it can change the contents of the object to which it points. La plupart des applications, cela est suffisant et génère le comportement souhaité.For most applications this is sufficient and yields the desired behavior.

Si une méthode doit retourner une instance différente, utilisez la valeur de retour de la méthode pour y parvenir.If a method must return a different instance, use the return value of the method to accomplish this. Consultez la System.String classe pour diverses méthodes qui opèrent sur des chaînes et retourner une nouvelle instance d’une chaîne.See the System.String class for a variety of methods that operate on strings and return a new instance of a string. Lorsque ce modèle est utilisé, l’appelant doit décider si l’objet d’origine est conservé.When this model is used, the caller must decide whether the original object is preserved.

Bien que les valeurs de retour sont courants et très utilisées, l’application correcte de out et ref paramètres requiert de conception intermédiaire et de compétences en matière de codage.Although return values are commonplace and heavily used, the correct application of out and ref parameters requires intermediate design and coding skills. Architectes de bibliothèque de conception destiné à une audience générale ne doit pas s’attendre aux utilisateurs maîtrisent l’utilisation des out ou ref paramètres.Library architects who design for a general audience should not expect users to master working with out or ref parameters.

Comment corriger les violationsHow to Fix Violations

Pour corriger une violation de cette règle est dû à un type valeur, ont la méthode retourne l’objet comme sa valeur de retour.To fix a violation of this rule that is caused by a value type, have the method return the object as its return value. Si la méthode doit retourner plusieurs valeurs, reconcevoir pour retourner une seule instance d’un objet qui conserve les valeurs.If the method must return multiple values, redesign it to return a single instance of an object that holds the values.

Pour corriger une violation de cette règle est dû à un type référence, assurez-vous que le comportement souhaité doit retourner une nouvelle instance de la référence.To fix a violation of this rule that is caused by a reference type, make sure that the desired behavior is to return a new instance of the reference. S’il s’agit, la méthode doit utiliser sa valeur de retour pour ce faire.If it is, the method should use its return value to do this.

Quand supprimer les avertissementsWhen to Suppress Warnings

Il est possible de supprimer un avertissement de cette règle.It is safe to suppress a warning from this rule. Toutefois, cette conception peut entraîner des problèmes d’utilisation.However, this design could cause usability issues.

ExempleExample

La bibliothèque suivante présente deux implémentations d’une classe qui génère des réponses aux commentaires d’un utilisateur.The following library shows two implementations of a class that generates responses to the feedback of a user. La première implémentation (BadRefAndOut) force l’utilisateur de la bibliothèque à gérer trois valeurs de retour.The first implementation (BadRefAndOut) forces the library user to manage three return values. La seconde implémentation (RedesignedRefAndOut) simplifie l’expérience utilisateur en retournant une instance d’une classe de conteneur (ReplyData) qui gère les données comme une unité unique.The second implementation (RedesignedRefAndOut) simplifies the user experience by returning an instance of a container class (ReplyData) that manages the data as a single unit.

using System;

namespace DesignLibrary
{
   public enum Actions
   {
      Unknown,
      Discard,
      ForwardToManagement,
      ForwardToDeveloper
   }

   public enum TypeOfFeedback
   {
      Complaint, 
      Praise,
      Suggestion,
      Incomprehensible
   }

   public class BadRefAndOut
   {
      // Violates rule: DoNotPassTypesByReference.

      public static bool ReplyInformation (TypeOfFeedback input, 
         out string reply, ref Actions action)
      {
         bool returnReply = false;
         string replyText = "Your feedback has been forwarded " + 
                            "to the product manager.";

         reply = String.Empty;
         switch (input)
         {
            case TypeOfFeedback.Complaint:
            case TypeOfFeedback.Praise :
               action = Actions.ForwardToManagement;
               reply = "Thank you. " + replyText;
               returnReply = true;
               break;
            case TypeOfFeedback.Suggestion:
               action = Actions.ForwardToDeveloper;
               reply = replyText;
               returnReply = true;
               break;
            case TypeOfFeedback.Incomprehensible:
            default:
               action = Actions.Discard;
               returnReply = false;
               break;
         }
         return returnReply;
      }
   }

   // Redesigned version does not use out or ref parameters;
   // instead, it returns this container type.

   public class ReplyData
   {
      string reply;
      Actions action;
      bool returnReply;
      
      // Constructors.
      public ReplyData()
      {
         this.reply = String.Empty;
         this.action = Actions.Discard;
         this.returnReply = false;
      }

      public ReplyData (Actions action, string reply, bool returnReply)
      {
         this.reply = reply;
         this.action = action;
         this.returnReply = returnReply;
      }

      // Properties.
      public string Reply { get { return reply;}}
      public Actions Action { get { return action;}}

      public override string ToString()
      {
         return String.Format("Reply: {0} Action: {1} return? {2}", 
            reply, action.ToString(), returnReply.ToString());
      }
   }

   public class RedesignedRefAndOut
   {
      public static ReplyData ReplyInformation (TypeOfFeedback input)
      {
         ReplyData answer;
         string replyText = "Your feedback has been forwarded " + 
            "to the product manager.";

         switch (input)
         {
            case TypeOfFeedback.Complaint:
            case TypeOfFeedback.Praise :
               answer = new ReplyData(
                  Actions.ForwardToManagement,
                  "Thank you. " + replyText,
                  true);
               break;
            case TypeOfFeedback.Suggestion:
               answer =  new ReplyData(
                  Actions.ForwardToDeveloper,
                  replyText,
                  true);
               break;
            case TypeOfFeedback.Incomprehensible:
            default:
               answer = new ReplyData();
               break;
         }
         return answer;
      }
   }
}

ExempleExample

L’application suivante illustre l’expérience de l’utilisateur.The following application illustrates the experience of the user. L’appel à la nouvelle bibliothèque (UseTheSimplifiedClass méthode) est plus simple, et les informations retournées par la méthode sont facile à gérer.The call to the redesigned library (UseTheSimplifiedClass method) is more straightforward, and the information returned by the method is easily managed. La sortie des deux méthodes est identique.The output from the two methods is identical.

using System;

namespace DesignLibrary
{
   public class UseComplexMethod
   {
      static void UseTheComplicatedClass()
      {
         // Using the version with the ref and out parameters. 
         // You do not have to initialize an out parameter.

         string[] reply = new string[5];

         // You must initialize a ref parameter.
         Actions[] action = {Actions.Unknown,Actions.Unknown,
                             Actions.Unknown,Actions.Unknown,
                             Actions.Unknown,Actions.Unknown}; 
         bool[] disposition= new bool[5];
         int i = 0;

         foreach(TypeOfFeedback t in Enum.GetValues(typeof(TypeOfFeedback)))
         {
            // The call to the library.
            disposition[i] = BadRefAndOut.ReplyInformation(
               t, out reply[i], ref action[i]);
            Console.WriteLine("Reply: {0} Action: {1}  return? {2} ", 
               reply[i], action[i], disposition[i]);
            i++;
         }
      }

      static void UseTheSimplifiedClass()
      {
         ReplyData[] answer = new ReplyData[5];
         int i = 0;
         foreach(TypeOfFeedback t in Enum.GetValues(typeof(TypeOfFeedback)))
         {
            // The call to the library.
            answer[i] = RedesignedRefAndOut.ReplyInformation(t);
            Console.WriteLine(answer[i++]);
         }
      }

      public  static void Main()
      {
         UseTheComplicatedClass();

         // Print a blank line in output.
         Console.WriteLine("");

         UseTheSimplifiedClass();
      }
   }
}

ExempleExample

La bibliothèque de l’exemple suivant illustre comment ref paramètres pour les types référence sont utilisés et affiche un meilleur moyen d’implémenter cette fonctionnalité.The following example library illustrates how ref parameters for reference types are used and shows a better way to implement this functionality.

using System;

namespace DesignLibrary
{
   public class ReferenceTypesAndParameters
   {

      // The following syntax will not work. You cannot make a
      // reference type that is passed by value point to a new
      // instance. This needs the ref keyword.

      public static void BadPassTheObject(string argument)
      {
         argument = argument + " ABCDE";
      }

      // The following syntax will work, but is considered bad design.
      // It reassigns the argument to point to a new instance of string.
      // Violates rule DoNotPassTypesByReference.

      public static void PassTheReference(ref string argument)
      {
         argument = argument + " ABCDE";
      }

      // The following syntax will work and is a better design.
      // It returns the altered argument as a new instance of string.

      public static string BetterThanPassTheReference(string argument)
      {
         return argument + " ABCDE";
      }
   }
}

ExempleExample

L’application suivante appelle chaque méthode dans la bibliothèque pour illustrer le comportement.The following application calls each method in the library to demonstrate the behavior.

using System;

namespace DesignLibrary
{
   public class Test
   {
      public static void Main()
      {
         string s1 = "12345";
         string s2 = "12345";
         string s3 = "12345";
     
         Console.WriteLine("Changing pointer - passed by value:");
         Console.WriteLine(s1);
         ReferenceTypesAndParameters.BadPassTheObject (s1);
         Console.WriteLine(s1);

         Console.WriteLine("Changing pointer - passed by reference:");
         Console.WriteLine(s2);
         ReferenceTypesAndParameters.PassTheReference (ref s2);
         Console.WriteLine(s2);

         Console.WriteLine("Passing by return value:");
         s3 = ReferenceTypesAndParameters.BetterThanPassTheReference (s3);
         Console.WriteLine(s3);
      }
   }
}

Cet exemple produit la sortie suivante.This example produces the following output.

Pointeur de modification - passé par valeur : 12345 12345 pointeur Changing, passé par référence : ** 12345** ABCDE 12345 passage par valeur de retour : ABCDE 12345Changing pointer - passed by value: 12345 12345 Changing pointer - passed by reference: 12345 12345 ABCDE Passing by return value: 12345 ABCDE

Essayez les méthodes de modèleTry pattern methods

DescriptionDescription

Les méthodes qui implémentent le essayez<quelque chose > de modèle, par exemple System.Int32.TryParse, ne déclenchent pas cette violation.Methods that implement the Try<Something> pattern, such as System.Int32.TryParse, do not raise this violation. L’exemple suivant montre une structure (type valeur) qui implémente le System.Int32.TryParse (méthode).The following example shows a structure (value type) that implements the System.Int32.TryParse method.

CodeCode

using System;

namespace Samples
{
    public struct Point
    {
        private readonly int _X;
        private readonly int _Y;

        public Point(int axisX, int axisY)
        {
            _X = axisX;
            _Y = axisY;
        }
        
        public int X
        {
            get { return _X; }
        }

        public int Y
        {
            get { return _Y; }
        }

        public override int GetHashCode()
        {
            return _X ^ _Y;
        }

        public override bool Equals(object obj)
        {
            if (!(obj is Point))
                return false;

            return Equals((Point)obj);
        }

        public bool Equals(Point other)
        {
            if (_X != other._X)
                return false;

            return _Y == other._Y;
        }

        public static bool operator ==(Point point1, Point point2)
        {
            return point1.Equals(point2);
        }

        public static bool operator !=(Point point1, Point point2)
        {
            return !point1.Equals(point2);
        }

        // Does not violate this rule
        public static bool TryParse(string value, out Point result)
        {
            // TryParse Implementation
            result = new Point(0,0);
            return false;
        }
    }
}

CA1045 : Ne pas passer de types par référenceCA1045: Do not pass types by reference