CA1021: Unikanie parametrów wyjściowych
TypeName |
AvoidOutParameters |
CheckId |
CA1021 |
Kategoria |
Microsoft.Design |
Złamanie zmiany |
Złamanie |
Przyczyna
Metody publiczne lub chronione w typie publicznych ma out parametru.
Opis reguły
Przekazywanie typów przez odwołanie (za pomocą out lub ref) wymaga doświadczenia ze wskaźnikami, zrozumienia, jak różnią się typów wartości i typy odwołań i obsługi metod z wielu wartości zwracanej.Ponadto, różnica między out i ref parametrów nie jest powszechnie zrozumiałe.
Gdy typ odwołania jest przekazywana "przez odwołanie", metoda zamierza użyć parametru, aby powrócić do innego wystąpienia obiektu.Przekazując typ odwołania, przez odniesienie znany także jako wskaźnikiem podwójny wskaźnik do wskaźnika lub podwójne pośrednia.Przy użyciu domyślnego wywoływania Konwencji, który jest przekazywany "za pomocą wartości", parametr, który ma typ odwołania już otrzymuje wskaźnik na obiekt.Wskaźnik, a nie obiektu, na które wskazuje, jest przekazywany przez wartość.Przechodzą przez oznacza wartość, że metoda nie można zmienić wskaźnik, aby go wskaż nowe wystąpienie typu odwołania.Jednakże można to zmienić zawartość obiektu, w którym punkty.Dla większości aplikacji jest to wystarczające i daje pożądane zachowanie.
Jeśli metoda musi zwrócić różnych instancji, użyj wartość zwracana przez metodę, aby osiągnąć ten cel.Zobacz String klasy dla różnych metod, które działają na ciągi znaków i zwracać nową instancję ciągu.Gdy używany jest ten model, obiekt wywołujący należy zdecydować, czy jest zachowywany oryginalny obiekt.
Mimo że zwracane wartości są powszechne i intensywnie wykorzystywane, właściwego stosowania out i ref wymaga parametrów, pośrednie projektowania i kodowania umiejętności.Architektów biblioteki, którzy projektowania dla odbiorców ogólne nie należy się spodziewać użytkowników do wzorca roboczego z out lub ref parametry.
Jak naprawić naruszenia
Aby naprawić naruszenie tej zasady, powodowanego przez typ wartości, mają metodę zwraca obiekt jako swojej zwracanej wartości.Metoda musi zwracać wiele wartości, ponownie zaprojektować powrót pojedyncze wystąpienie obiektu, który przechowuje wartości.
Aby rozwiązać naruszenie tej zasady, powodowanego przez typ odwołania, upewnij się, pożądane zachowanie ma zwracać nową instancję odniesienia.Jeśli jest, metody należy użyć swojej zwracanej wartooci w tym celu.
Kiedy do pomijania ostrzeżenia
Bezpiecznie wyłączyć ostrzeżenia od tej reguły.Jednakże ten projekt może spowodować problemy użyteczność.
Przykład
Następująca biblioteka zawiera dwa implementacje klasy, która generuje odpowiedzi na opinie użytkowników.Pierwsze wykonania (BadRefAndOut) wymusza na użytkowniku biblioteki i zarządzać trzy wartości zwracanej.Druga implementacja (RedesignedRefAndOut) upraszcza czynności użytkownika przez zwrócenie instancji klasy pojemnika (ReplyData), zarządza danych jako pojedyncza jednostka.
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;
}
}
}
Następująca aplikacja ilustruje doświadczenia użytkownika.Wywołanie biblioteki przeprojektowany (UseTheSimplifiedClass metoda) jest łatwiejsza, oraz informacji, zwracany przez metodę można łatwo zarządzać.Dane wyjściowe z dwóch metod jest identyczna.
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();
}
}
}
Biblioteka następujący przykład ilustruje sposób ref parametry dla typu odwołania są używane i pokazuje lepszy sposób zaimplementować tę funkcję.
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";
}
}
}
Następujące aplikacja wywołuje każdej metody w bibliotece, aby wykazać zachowanie.
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);
}
}
}
Ten przykład generuje następujące wyniki.
Spróbuj metody wzorca
Opis
Metody, które implementują Spróbuj <Something> wzorek, takich jak Int32.TryParse, nie podnoszą tego naruszenia.W poniższym przykładzie pokazano strukturę (typ wartości), który implementuje Int32.TryParse metody.
Kod
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;
}
}
}