CA1021: evitar parâmetros de saídaCA1021: Avoid out parameters

NomeDoTipoTypeName AvoidOutParametersAvoidOutParameters
CheckIdCheckId CA1021CA1021
CategoriaCategory Microsoft.DesignMicrosoft.Design
Alteração SignificativaBreaking Change QuebraBreaking

CausaCause

Um método público ou protegido em um tipo público tem um out parâmetro.A public or protected method in a public type has an out parameter.

Descrição da regraRule description

Passar tipos por referência (usando out ou ref) requer experiência com ponteiros, compreensão das diferem entre tipos de valor e tipos de referência e métodos de tratamento com vários valores de retorno.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. Além disso, a diferença entre out e ref parâmetros não é amplamente compreendida.Also, the difference between out and ref parameters is not widely understood.

Quando um tipo de referência for passado "por referência", o método pretende usar o parâmetro para retornar uma instância diferente do objeto.When a reference type is passed "by reference," the method intends to use the parameter to return a different instance of the object. Passar um tipo de referência por referência também é conhecido como usando um ponteiro duplo, o ponteiro para um ponteiro ou indireção double.Passing a reference type by reference is also known as using a double pointer, pointer to a pointer, or double indirection. Usando o padrão de convenção de chamada, que é passada "por valor", um parâmetro que usa um tipo de referência já recebe um ponteiro para o 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. O ponteiro, não o objeto para o qual ele aponta, é passado por valor.The pointer, not the object to which it points, is passed by value. Passe por valor significa que o método não é possível alterar o ponteiro para que ele aponte para uma nova instância do tipo de referência.Pass by value means that the method cannot change the pointer to have it point to a new instance of the reference type. No entanto, ele pode alterar o conteúdo do objeto para o qual ele aponta.However, it can change the contents of the object to which it points. Para a maioria dos aplicativos, isso é suficiente e produz o comportamento desejado.For most applications this is sufficient and yields the desired behavior.

Se um método deve retornar uma instância diferente, use o valor de retorno do método para fazer isso.If a method must return a different instance, use the return value of the method to accomplish this. Consulte o System.String classe para uma variedade de métodos que operam em cadeias de caracteres e retornam uma nova instância de uma cadeia de caracteres.See the System.String class for a variety of methods that operate on strings and return a new instance of a string. Quando esse modelo é usado, o chamador deve decidir se o objeto original é preservado.When this model is used, the caller must decide whether the original object is preserved.

Embora os valores de retorno são comuns e usadas intensamente, a aplicação correta de out e ref parâmetros requer habilidades de codificação e design intermediário.Although return values are commonplace and heavily used, the correct application of out and ref parameters requires intermediate design and coding skills. Os arquitetos de bibliotecas que projetam para um público em geral não deve esperar que os usuários dominem o trabalho com out ou ref parâmetros.Library architects who design for a general audience should not expect users to master working with out or ref parameters.

Como corrigir violaçõesHow to fix violations

Para corrigir uma violação dessa regra que é causado por um tipo de valor, fazer com que o método retornar o objeto como seu valor de retorno.To fix a violation of this rule that is caused by a value type, have the method return the object as its return value. Se o método deve retornar vários valores, recrie-o para uma única instância de um objeto que contém os valores de retorno.If the method must return multiple values, redesign it to return a single instance of an object that holds the values.

Para corrigir uma violação dessa regra que é causado por um tipo de referência, certifique-se de que o comportamento desejado é para retornar uma nova instância da referência.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. Se for, o método deve usar seu valor de retorno para fazer isso.If it is, the method should use its return value to do this.

Quando suprimir avisosWhen to suppress warnings

É seguro suprimir um aviso nessa regra.It is safe to suppress a warning from this rule. No entanto, esse design pode causar problemas de usabilidade.However, this design could cause usability issues.

ExemploExample

A biblioteca a seguir mostra duas implementações de uma classe que gera respostas aos comentários de um usuário.The following library shows two implementations of a class that generates responses to the feedback of a user. A primeira implementação (BadRefAndOut) força o usuário da biblioteca para gerenciar os três valores de retorno.The first implementation (BadRefAndOut) forces the library user to manage three return values. A implementação de segundo (RedesignedRefAndOut) simplifica a experiência do usuário, retornando uma instância de uma classe de contêiner (ReplyData) que gerencia os dados como uma única unidade.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;
      }
   }
}

ExemploExample

O aplicativo a seguir ilustra a experiência do usuário.The following application illustrates the experience of the user. A chamada para a biblioteca reprojetada (UseTheSimplifiedClass método) é mais simples, e as informações retornadas pelo método é facilmente gerenciadas.The call to the redesigned library (UseTheSimplifiedClass method) is more straightforward, and the information returned by the method is easily managed. A saída entre os dois métodos é idêntica.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();
      }
   }
}

ExemploExample

A biblioteca de exemplo a seguir ilustra como ref parâmetros para tipos de referência são usados e mostra uma maneira melhor para implementar essa funcionalidade.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";
      }
   }
}

ExemploExample

O aplicativo a seguir chama cada método na biblioteca para demonstrar o comportamento.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 exemplo gera a seguinte saída: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

Tente os métodos padrãoTry pattern methods

DescriçãoDescription

Métodos que implementam o tente<algo > padrão, tais como System.Int32.TryParse, não gere a essa violação.Methods that implement the Try<Something> pattern, such as System.Int32.TryParse, do not raise this violation. O exemplo a seguir mostra uma estrutura (tipo de valor) que implementa o 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: não passar tipos por referênciaCA1045: Do not pass types by reference