Compartilhar via


Não passe os tipos por referência

TypeName

DoNotPassTypesByReference

CheckId

CA1045

Category (Categoria)

Microsoft.Design

Quebrando alterar

Quebrando

Causa

Um método público ou protegido em um tipo público possui um ref parâmetro que leva um tipo primitivo, um tipo de referência ou um tipo de valor que não é um dos tipos internos.

Descrição da regra

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

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.(Um tipo de referência de passar por referência também é conhecido sistema autônomo usando um ponteiro duplo, ponteiro para um ponteiro ou dupla de indireção.) Usando o padrão chamando convenção, que é passar "pelo valor", um parâmetro que leva um tipo de referência já recebe um ponteiro para o objeto.O ponteiro, não o objeto ao qual ele aponta, é passado por valor.Passar por valor significa que o método não é possível alterar o ponteiro para que ele aponte para uma nova instância da referência de digitar, mas pode alterar o Sumário do objeto ao qual ele aponta.Para a maioria dos aplicativos é suficiente e gera o comportamento desejado.

Se um método deve retornar uma instância diferente, use o valor retornado do método para fazer isso.Consulte o System.String classe de uma variedade de métodos que operam em seqüências de caracteres e uma nova instância de uma seqüência de caracteres de retorno. Usando este modelo, ela é deixada para o chamador para decidir se o objeto original é preservado.

Embora os valores de retorno são comuns e muito usado, a aplicação correta de out e ref parâmetros requer design intermediário e técnicas de codificação. Arquitetos de biblioteca que criar para um audiência em geral não devem esperar que os usuários mestre trabalhar com out ou ref parâmetros.

Observação:

Quando você trabalhar com parâmetros que são grandes estruturas, os recursos adicionais que são necessário para copiar essas estruturas poderia causa um desempenho de impacto quando Você passe por valor.Nesses casos, você poderá considerar o uso ref ou out parâmetros.

Como corrigir violações

Para corrigir uma violação dessa regra causada por um tipo de valor, ter o método retornar o objeto sistema autônomo valor retornado.Se o método deve retornar vários valores, recrie-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 retornando uma nova instância da referência é o comportamento desejado.Se for, o método deve usar o valor retornado para fazer isso.

Quando suprimir avisos

É seguro eliminar um aviso da regra; no entanto, esse design pode gerar problemas de usabilidade.

Exemplo

A seguinte biblioteca mostra duas implementações de uma classe que gera respostas aos comentários do usuário.A primeira implementação (BadRefAndOut) força o usuário da biblioteca para gerenciar os três valores de retorno. A implementação segunda (RedesignedRefAndOut) simplifica a experiência do usuário, retornando uma instância de um (classe de contêinerReplyData) que gerencia sistema autônomo dados sistema autônomo 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 a seguir ilustra a experiência do usuário.A telefonar para a biblioteca reprojetada (UseTheSimplifiedClass método) é mais simples e as informações retornadas pelo método é com com facilidade gerenciadas. A saída dos dois métodos é idêntica.

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

A seguinte biblioteca de exemplo ilustra como ref parâmetros de tipos de referência são usados e mostra uma melhor maneira 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.

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

Regras relacionadas

Evite os parâmetros de saída