CA1045: Nepředávejte typy odkazem
TypeName |
DoNotPassTypesByReference |
CheckId |
CA1045 |
Kategorie |
Microsoft.Design |
Narušující změna |
Narušující |
Příčina
Veřejné nebo chráněná metoda veřejné typu ref parametr, který má primitivní typ, typ odkazu nebo typ hodnoty, který je jeden z předdefinovaných typů.
Popis pravidla
Typy předání odkazem (pomocí out nebo ref) vyžaduje zkušenosti s ukazatele, vysvětlení, jak se liší hodnoty a odkazové typy a metody, které mají více vrácené hodnoty zpracování.Také rozdíl mezi out a ref parametry široce porozuměno.
Pokud je předán odkaz typu "odkazem" v úmyslu vrátit jinou instanci objektu pomocí parametru metody. (Předání odkazem typu odkazu se označuje také jako pomocí dvojité ukazatel ukazatel ukazatel nebo dvojité dereference.) Parametr, který již má typ odkazu pomocí výchozího volání úmluvy, které se předávají "hodnotou", přijímá ukazatel na objekt.Ukazatel není objekt, na který odkazuje, je předán podle hodnoty.Předání hodnotou znamená, že metodu nelze změnit ukazatel na něm ukazatel na novou instanci odkaz zadejte však můžete změnit obsah objektu, na který odkazuje.Pro většinu aplikací je dostatečná a dává chování, které chcete.
Pokud metoda musí vrátit jinou instanci, použijte k tomuto účelu vrácenou hodnotu metody.Najdete String třídy pro různé metody, které fungují na řetězce a vrátí novou instanci řetězce.Pomocí tohoto modelu je ponechána rozhodnout, zda je zachována původní objekt volající.
Přestože vrácené hodnoty jsou běžné a nejčastěji používá správné uplatňování out a ref parametry vyžaduje vnitřní design a kódování kvalifikace.Knihovna architektů, kteří sestavují pro diváky by neměl předpokládat uživatelům hlavní práce s out nebo ref parametry.
[!POZNÁMKA]
Při práci s parametry, které jsou velké struktury způsobit dodatečné prostředky, které jsou nutné ke kopírování těchto struktur vliv na výkon při předání hodnotou.V těchto případech můžete zvážit použití ref nebo out parametry.
Jak vyřešit porušení
Stanovit porušení tohoto pravidla, která je způsobena typu hodnoty mají metoda vrátí objekt jako vrácená hodnota.Metoda musí vrátit více hodnot, změnit návrh ho vrátit jednu instanci objektu, který obsahuje hodnoty.
Stanovit porušení tohoto pravidla, která je způsobena typ odkazu ujistěte se, že je chování, které chcete vrátit novou instanci odkaz.Pokud je, by k tomu použít její vrácená hodnota metody.
Kdy potlačit upozornění
Je možné potlačit upozornění od tohoto pravidla; Tento návrh však může způsobit problémy s použitelností.
Příklad
Následující knihovna zobrazuje dvě implementace třídy, který generuje reakce na názory uživatele.První provedení (BadRefAndOut) donutí uživatele knihovny pro správu tři vrácené hodnoty.Druhé provedení (RedesignedRefAndOut) zjednodušuje uživatelské rozhraní vrátit instance třídy kontejneru (ReplyData), spravuje data jako jeden celek.
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;
}
}
}
Následující aplikace ukazuje zkušenost uživatele.Volání upravené knihovny (UseTheSimplifiedClass metoda) je přímější, a informace, který je vrácen metodou je snadno spravován.Výstup z dvou metod je stejná.
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();
}
}
}
Knihovna následující příklad ukazuje, jak ref parametry pro odkazové typy jsou používány a ukazuje lepší způsob, jak implementovat tuto funkci.
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";
}
}
}
Následující aplikace volá každou metodu v knihovně prokázat chování.
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);
}
}
}
Tento příklad vytvoří následující výstup.