Udostępnij za pośrednictwem


CA1045: Nie przekazuj typów przez odwołanie

TypeName

DoNotPassTypesByReference

CheckId

CA1045

Kategoria

Microsoft.Design

Zmiana kluczowa

Kluczowa

Przyczyna

Metoda publiczna lub chroniona w typie publicznym ma parametr ref, który przyjmuje typ pierwotny, referencyjny lub wartościowy, który nie jest jednym z typów wbudowanych.

Opis reguły

Przekazywanie typów przez odwołanie (używając out lub ref) wymaga doświadczenia ze wskaźnikami, rozumienia różnicy między typami wartości i typami odwołania oraz obsługi metod, które mają wiele wartości zwracanych.Ponadto różnica między parametrami out i ref nie jest powszechnie zrozumiała.

Gdy typ referencyjny jest przekazywany "przez referencję", metoda zamierza użyć parametru, aby zwrócić inne wystąpienie obiektu. (Przekazywanie przez referencję typy referencyjnego jest również znane jako używanie podwójnego wskaźnika, wskaźnika na wskaźnik lub podwójne wyłuskanie) Przy użyciu domyślnej konwencji wywołania, która przekazuje "przez wartość", parametr który przyjmuje typ referencyjny, już otrzymuje wskaźnik do obiektu.Wskaźnik, a nie obiekt na który wskazuje, jest przekazywany przez wartość.Przekazywanie przez wartość oznacza, że metoda nie może zmienić wskaźnika tak, żeby wskazywał na nowe wystąpienie typu referencyjnego, ale może zmienić zawartość obiektu, na który wskazuje.Dla większości aplikacji jest wystarczająca i dostarcza zachowanie, które jest potrzebne.

Jeśli metoda musi zwrócić inne wystąpienie, należy użyć wartości zwracanej metody aby to osiągnąć.Zobacz klasę String dla różnych metod, które działają na ciągach i zwracają nowe wystąpienia ciągów.Przy użyciu tego modelu, pozostawia się wywołującemu możliwość decyzji, czy obiekt oryginalny jest zachowywany.

Chociaż wartości zwracane są powszechne i intensywnie wykorzystywane, poprawne stosowanie parametrów out i ref wymaga średniozaawansowanego projektu i umiejętności kodowania.Architekci biblioteki, którzy tworzą dla wszystkich nie powinni oczekiwać od użytkowników dobrej znajomości parametrów out lub ref.

[!UWAGA]

Podczas pracy z parametrami, które są dużymi strukturami, dodatkowe zasoby, które są wymagane do kopiowania tych struktur mogą wpływać na wydajność podczas przekazywania przez wartość.W tych przypadkach należy rozważyć użycie parametrów ref lub out.

Jak naprawić naruszenia

Aby naprawić naruszenie tej reguły, spowodowane przez typ wartościowy, metoda powinna zwracać obiekt jako wartość zwracaną.Jeśli metoda musi zwracać wiele wartości, należy ją przeprojektować aby zwracała pojedyncze wystąpienie obiektu, który przechowuje te wartości.

Aby naprawić naruszenie tej zasady, spowodowane przez typ referencyjny, upewnij się że zachowanie, które jest pożądane to zwracanie nowego wystąpienia referencji.Jeśli tak jest, metoda powinna używać w tym celu wartości zwracanej.

Kiedy pominąć ostrzeżenia

Bezpiecznie jest pominąć ostrzeżenia dotyczące tej reguły; jednak taki projekt może spowodować problemy z użytecznością.

Przykład

Następująca biblioteka zawiera dwa implementacje klasy, która generuje odpowiedzi na opinie użytkownika.Pierwsza implementacja (BadRefAndOut) wymusza na użytkowniku biblioteki użycie trzech wartości zwracanych.Druga implementacja (RedesignedRefAndOut) upraszcza czynności użytkownika przez zwrócenie wystąpienia klasy kontenera (ReplyData), która zarządza danymi 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 przeprojektowanej biblioteki (metoda UseTheSimplifiedClass) jest bardziej bezpośrednie i informacja, która jest zwracana przez metodę jest łatwa do zarządzania.Wyjście z obu metod jest identyczne.

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

Następująca przykładowa biblioteka ilustruje, jak parametry ref są używane w przypadku typów referencyjnych i pokazuje lepszy sposób implementacji tej funkcjonalności.

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żdą metodę z biblioteki, aby zademonstrować 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.

  

Powiązane reguły

CA1021: Unikanie parametrów wyjściowych