Compartilhar via


CA1021: evitar parâmetros de saída

TypeName

AvoidOutParameters

CheckId

CA1021

Categoria

Microsoft.Design

Alteração Significativa

Quebra

Causa

Um público ou um método protegido em um tipo utilitário têm um parâmetro de out .

Descrição da Regra

Passar tipos por referência (usando out ou ref) requer experiência com ponteiros, compreender como os tipos de valor e os tipos de referência são diferentes, e os métodos de manipulação com vários valores de retorno.Além disso, a diferença entre out e os parâmetros de ref não são compreendidos muito.

Quando um tipo de referência é passado por referência “,” o método pretende usar o parâmetro para retornar uma instância diferente do objeto.Passando um tipo de referência por referência também é conhecido como usar um ponteiro vezes, o ponteiro para um ponteiro, ou de nomes indiretos duplo.Usando a convenção padrão de chamada, que é passagem pelo valor “,” um parâmetro que usa um tipo de referência já recebe um ponteiro para o objeto.O ponteiro, não ao objeto que aponta, é passado pelo valor.A passagem pelo valor significa que o método não pode alterar o ponteiro para o que aponte para uma nova instância do tipo de referência.No entanto, pode modificar o conteúdo do objeto ao qual aponta.Para a maioria dos aplicativos isso é suficiente e gerencie o comportamento desejado.

Se um método deve retornar uma instância diferente, use o valor de retorno do método para fazer isso.Consulte a classe de String para uma variedade de métodos que operam em cadeias de caracteres e retornam uma nova instância de uma cadeia de caracteres.Quando esse modelo é usado, o chamador deve decidir se o objeto original é preservado.

Embora os valores de retorno são comuns e intensamente usado, o aplicativo correto de out e os parâmetros de ref exigem habilidades intermediários de design e de codificação.Os arquitetos de biblioteca que criam para um público geral não devem esperar que os usuários dominar trabalhar com out ou parâmetros de ref .

Como Corrigir Violações

Para corrigir uma violação dessa regra é causada por um tipo de valor, tem o retorno do método do objeto como seu valor de retorno.Se o método deve retornar vários valores, remodele-o para retornar uma única instância de um objeto que contém os valores.

Para corrigir uma violação dessa regra é causada por um tipo de referência, certifique-se de que o comportamento desejado é retornar uma nova instância de referência.Se for, o método deve usar o valor de retorno para fazer isso.

Quando Suprimir Alertas

É seguro suprimir um aviso desta regra.No entanto, esse design pode causar problemas de usabilidade.

Exemplo

A biblioteca seguir mostra duas implementações de uma classe que gerencia respostas aos comentários de um usuário.A primeira implementação (BadRefAndOut) força o usuário de biblioteca para gerenciar três valores de retorno.A segunda implementação (RedesignedRefAndOut) simplifica a experiência do usuário retornando uma instância de uma classe do contêiner (ReplyData) que gerencia os dados como uma única unidade.

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;
      }
   }
}

O aplicativo seguir ilustra a experiência do usuário.A chamada para a biblioteca alto (método deUseTheSimplifiedClass ) é mais simples, e as informações retornada pelo método é gerenciado facilmente.A saída dos dois métodos são idênticas.

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();
      }
   }
}

Biblioteca de exemplo a seguir ilustra como os parâmetros de ref para tipos de referência são usados e mostra uma maneira ideal de implementar essa funcionalidade.

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";
      }
   }
}

O seguinte aplicativo chama cada método na biblioteca para demonstrar o comportamento.

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);
      }
   }
}

O exemplo produz a seguinte saída.

  

Métodos de padrão da tentativa

Descrição

Os métodos que implementam o padrão de Tentativa<Algo> , como Int32.TryParse, não lançam essa violação.O exemplo a seguir mostra uma estrutura (tipo de valor) que implementa o método de Int32.TryParse .

Código

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;
        }
    }
}

Regras Relacionadas

CA1045: não passar tipos por referência