CA1021: Výstupní parametry vyhnout se
Název_typu |
AvoidOutParameters |
CheckId |
CA1021 |
Kategorie |
Microsoft.design |
Změnit rozdělení |
Rozdělení |
Příčina
Veřejné nebo chráněná metoda veřejné typu out parametr.
Popis pravidla
Typy předání odkazem (pomocí out nebo ref) vyžaduje zkušenosti s ukazatele vysvětlení, jak se liší typy hodnoty a typy odkazů a manipulaci s více vrácené hodnoty metody.Také rozdíl mezi out a ref parametry široce nebyla rozpoznána.
Typ odkazu je předána "odkazem", zamýšlí se metoda vrátí jinou instanci objektu pomocí parametru.Typ odkazu předání odkazem je také označován jako použití dvojité ukazatele, ukazatele ukazatel nebo dvojité nepřímé odkazování.Pomocí výchozí volání úmluvy je předat "hodnotou", obdrží parametr, který typ odkazu trvá již ukazatel na objekt.Ukazatel není objekt, na který odkazuje, je předání hodnotou.Předávání hodnota znamená, že metoda nemůže změnit ukazatel ji přejděte na novou instanci typu odkazu.Můžete však změnit obsah objektu, na který odkazuje.Pro většinu aplikací je dostatečná a dává požadované chování.
Metoda musí vracet různé instance, dosáhnout pomocí hodnota vrácená metodou.Najdete System.String třídy pro různé metody, které pracují řetězce a vrátí novou instanci řetězce.Při použití tohoto modelu volající musí rozhodnout, zda je zachováno původní objekt.
Přestože vrácené hodnoty jsou běžné a zatížen správné uplatňování out a ref parametry vyžaduje průběžné návrhu a kódování dovedností.Knihovna architekty, kteří návrh pro diváky není pravděpodobné, že uživatelé hlavní práce s out nebo ref parametry.
Jak opravit porušení
Oprava porušení tohoto pravidla, která je způsobena typ hodnoty, aby metoda vrátí objekt jako hodnoty.Metoda musí vrátit více hodnot, změnit návrh ji vrátit jednu instanci objektu s hodnotami.
Porušení tohoto pravidla, která je způsobena typ odkazu opravit, přesvědčte se, zda požadované chování vrátit novou instanci referenční.Pokud je metoda by k tomu použít hodnoty.
Při potlačení upozornění
Je bezpečné potlačí upozornění od tohoto pravidla.Tento návrh však může způsobit problémy použitelnosti.
Příklad
Následující knihovna zobrazuje dvě implementace třídy, který generuje reakce na názory uživatele.První implementace (BadRefAndOut) uživatel knihovny bude spravovat tři vrácené hodnoty.Provádění druhé (RedesignedRefAndOut) zjednodušuje uživatelské vracející instanci 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 jednoduchý více a je spravován informace vrácené metodou.Výstup ze dvou metod je shodné.
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 referenční typy se používají a lepší způsob provedení této funkce se zobrazí.
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.
Vyzkoušejte metody vzorek
Description
Metody, které implementují akci <Something> vzorek jako Int32.TryParse, ke zvýšení tohoto porušení.Následující příklad ukazuje strukturu (typ hodnota), který implementuje Int32.TryParse metoda.
Kód
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;
}
}
}