CA1021: Evitar parámetros outCA1021: Avoid out parameters

TypeNameTypeName AvoidOutParametersAvoidOutParameters
Identificador de comprobaciónCheckId CA1021CA1021
CategoríaCategory Microsoft.DesignMicrosoft.Design
Cambio problemáticoBreaking Change ProblemáticoBreaking

MotivoCause

Un método público o protegido en un tipo público tiene un out parámetro.A public or protected method in a public type has an out parameter.

Descripción de la reglaRule description

Pasar tipos por referencia (utilizando out o ref) requiere experiencia con punteros, saber la diferencia entre los tipos de valor y tipos de referencia y controlar métodos con varios valores devueltos.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. Además, la diferencia entre out y ref parámetros no se entiende ampliamente.Also, the difference between out and ref parameters is not widely understood.

Cuando se pasa un tipo de referencia "por referencia", el método intenta utilizar el parámetro para devolver una instancia diferente del objeto.When a reference type is passed "by reference," the method intends to use the parameter to return a different instance of the object. Pasar un tipo de referencia por referencia también se conoce como mediante un doble puntero, puntero a un puntero o direccionamiento indirecto doble.Passing a reference type by reference is also known as using a double pointer, pointer to a pointer, or double indirection. Utilizando el valor predeterminado convención de llamada, que se pasa "por valor", un parámetro que toma un tipo de referencia recibe un puntero al objeto.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. El puntero, no el objeto al que señala, se pasa por valor.The pointer, not the object to which it points, is passed by value. Pasar por valor significa que el método no puede cambiar el puntero para que señale a una nueva instancia del tipo de referencia.Pass by value means that the method cannot change the pointer to have it point to a new instance of the reference type. Sin embargo, puede cambiar el contenido del objeto al que señala.However, it can change the contents of the object to which it points. Para la mayoría de las aplicaciones, esto es suficiente y da como resultado el comportamiento deseado.For most applications this is sufficient and yields the desired behavior.

Si un método debe devolver una instancia diferente, utilice el valor devuelto del método para realizar esta acción.If a method must return a different instance, use the return value of the method to accomplish this. Consulte la System.String clase para una variedad de métodos que funcionan en cadenas y devolver una nueva instancia de una cadena.See the System.String class for a variety of methods that operate on strings and return a new instance of a string. Cuando se usa este modelo, el llamador debe decidir si mantiene el objeto original.When this model is used, the caller must decide whether the original object is preserved.

Aunque los valores devueltos son comunes y muy utilizados, la aplicación correcta de out y ref parámetros requiere un diseño intermedio y habilidades de programación.Although return values are commonplace and heavily used, the correct application of out and ref parameters requires intermediate design and coding skills. Arquitectos de bibliotecas para un público general no debería esperar que los usuarios dominen el uso de diseño out o ref parámetros.Library architects who design for a general audience should not expect users to master working with out or ref parameters.

Cómo corregir infraccionesHow to fix violations

Para corregir una infracción de esta regla que se debe a un tipo de valor, que el método devuelve el objeto como su valor devuelto.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 el método debe devolver varios valores, vuelva a diseñar para que devuelva una única instancia de un objeto que contiene los valores.If the method must return multiple values, redesign it to return a single instance of an object that holds the values.

Para corregir una infracción de esta regla que se debe a un tipo de referencia, asegúrese de que es el comportamiento deseado devolver una nueva instancia de la referencia.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. Si es así, el método debe utilizar su valor devuelto para hacerlo.If it is, the method should use its return value to do this.

Cuándo Suprimir advertenciasWhen to suppress warnings

Es seguro suprimir una advertencia de esta regla.It is safe to suppress a warning from this rule. Sin embargo, este diseño podría provocar problemas de facilidad de uso.However, this design could cause usability issues.

EjemploExample

La siguiente biblioteca muestra dos implementaciones de una clase que genera las respuestas a los comentarios de un usuario.The following library shows two implementations of a class that generates responses to the feedback of a user. La primera implementación (BadRefAndOut) obliga al usuario de la biblioteca para administrar los tres valores devueltos.The first implementation (BadRefAndOut) forces the library user to manage three return values. La segunda implementación (RedesignedRefAndOut) simplifica la experiencia del usuario al devolver una instancia de una clase de contenedor (ReplyData) que administra los datos como una sola unidad.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;
      }
   }
}

EjemploExample

La aplicación siguiente muestra la experiencia del usuario.The following application illustrates the experience of the user. La llamada a la biblioteca rediseñada (UseTheSimplifiedClass método) es más sencillo, y se administra fácilmente la información devuelta por el método.The call to the redesigned library (UseTheSimplifiedClass method) is more straightforward, and the information returned by the method is easily managed. El resultado de los dos métodos es idéntico.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();
      }
   }
}

EjemploExample

La biblioteca de ejemplo siguiente se muestra cómo ref parámetros para tipos de referencia se utilizan y se muestra una mejor manera de implementar esta funcionalidad.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";
      }
   }
}

EjemploExample

La siguiente aplicación llama a cada método en la biblioteca para mostrar el comportamiento.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);
      }
   }
}

Este ejemplo produce el siguiente resultado:This example produces the following output:

Changing pointer - passed by value:
12345
12345
Changing pointer - passed by reference:
12345
12345 ABCDE
Passing by return value:
12345 ABCDE

Pruebe los métodos de patrónTry pattern methods

DescripciónDescription

Los métodos que implementan el intente<algo > modelo, como System.Int32.TryParse, no producir esta infracción.Methods that implement the Try<Something> pattern, such as System.Int32.TryParse, do not raise this violation. El ejemplo siguiente muestra una estructura (tipo de valor) que implementa el System.Int32.TryParse método.The following example shows a structure (value type) that implements the System.Int32.TryParse method.

CódigoCode

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: No pasar tipos por referenciaCA1045: Do not pass types by reference