CA1045: não passar tipos por referência
TypeName |
DoNotPassTypesByReference |
CheckId |
CA1045 |
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 ref que usa um tipo primitivo, um tipo de referência, ou um tipo de valor que não é um dos tipos internos.
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 do que têm diversos 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.Passar 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, mas pode alterar 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.Usando esse modelo, é deixado ao chamador para 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 .
Observação |
---|
Quando você trabalha com parâmetros que são grandes estruturas, recursos adicionais que são necessários para copiar essas estruturas podem provocar um efeito de desempenho quando você passa pelo valor.Nesses casos, você poderia usar ref ou parâmetros de out . |
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 do 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.