Olvasás angol nyelven

Megosztás a következőn keresztül:


CA1021: A paraméterek elkerülése

Tulajdonság Érték
Szabályazonosító CA1021
Cím Paraméterek mellőzése
Kategória Tervez
A javítás kompatibilitástörő vagy nem törik Törés
Alapértelmezés szerint engedélyezve a .NET 9-ben Nem

Ok

Egy nyilvános vagy védett metódus paraméterrel rendelkezik out egy nyilvános típusban.

Ez a szabály alapértelmezés szerint csak külsőleg látható típusokat tekint meg, de ez konfigurálható.

Szabály leírása

A típusok hivatkozással (használatával out vagy refhasználatával) történő átadásához tapasztalatra van szükség a mutatókkal, az értéktípusok és a referenciatípusok különbségeinek megértéséhez, valamint a több visszatérési értékkel rendelkező metódusok kezeléséhez. Emellett a paraméterek és ref a paraméterek közötti out különbséget nem értjük széles körben.

Ha egy referenciatípust "hivatkozással" ad át, a metódus a paraméterrel az objektum egy másik példányát kívánja visszaadni. Hivatkozástípus hivatkozás alapján történő átadása kettős mutató, mutatóra mutató mutató vagy dupla indirekt irányú. Az alapértelmezett hívási konvencióval, amely "érték szerint" van átadva, egy hivatkozástípust használó paraméter már kap egy mutatót az objektumhoz. A mutatót, nem azt az objektumot, amelyre mutat, az érték adja át. Az érték szerinti átadás azt jelenti, hogy a metódus nem módosíthatja az egérmutatót úgy, hogy a hivatkozástípus új példányára mutasson. Azonban módosíthatja annak az objektumnak a tartalmát, amelyre mutat. A legtöbb alkalmazás esetében ez elegendő, és a kívánt viselkedést eredményezi.

Ha egy metódusnak egy másik példányt kell visszaadnia, ennek végrehajtásához használja a metódus visszatérési értékét. A sztringeken System.String működő és a sztringek új példányát visszaküldött metódusok osztályában talál. A modell használatakor a hívónak el kell döntenie, hogy az eredeti objektum megmarad-e.

Bár a visszatérési értékek gyakoriak és gyakran használatosak, a paraméterek és ref paraméterek helyes alkalmazása out köztes tervezési és kódolási készségeket igényel. Az általános közönség számára tervező könyvtártervezők nem várhatják el, hogy a felhasználók jártasak legyenek a használatban out vagy ref a paraméterekben.

Szabálysértések kijavítása

Az értéktípus által okozott szabály megsértésének kijavításához a metódus visszatérési értékként adja vissza az objektumot. Ha a metódusnak több értéket kell visszaadnia, újratervezheti úgy, hogy az értékeket tartalmazó objektum egyetlen példányát adja vissza.

A hivatkozástípus által okozott szabály megsértésének kijavításához győződjön meg arról, hogy a kívánt viselkedés a hivatkozás egy új példányának visszaadása. Ha igen, a metódusnak ehhez a visszatérési értékét kell használnia.

Mikor kell letiltani a figyelmeztetéseket?

A szabály figyelmeztetésének mellőzése biztonságos. Ez a kialakítás azonban használhatósági problémákat okozhat.

Figyelmeztetés mellőzése

Ha csak egyetlen szabálysértést szeretne letiltani, adjon hozzá előfeldolgozási irányelveket a forrásfájlhoz a szabály letiltásához és újbóli engedélyezéséhez.

#pragma warning disable CA1021
// The code that's violating the rule is on this line.
#pragma warning restore CA1021

Ha le szeretné tiltani egy fájl, mappa vagy projekt szabályát, állítsa annak súlyosságát none a konfigurációs fájlban.

[*.{cs,vb}]
dotnet_diagnostic.CA1021.severity = none

További információ: Kódelemzési figyelmeztetések letiltása.

Kód konfigurálása elemzéshez

A következő beállítással konfigurálhatja, hogy a kódbázis mely részein futtassa ezt a szabályt.

Ezt a beállítást konfigurálhatja csak ehhez a szabályhoz, az összes szabályhoz, vagy az ebben a kategóriában (Tervezés) szereplő összes szabályhoz, amelyekre vonatkozik. További információ: Kódminőségi szabály konfigurációs beállításai.

Adott API-felületek belefoglalása

A kódbázis azon részeit konfigurálhatja, amelyeken futtathatja ezt a szabályt az akadálymentességük alapján. Ha például meg szeretné adni, hogy a szabály csak a nem nyilvános API-felületen fusson, adja hozzá a következő kulcs-érték párot a projekt egyik .editorconfig fájljához:

dotnet_code_quality.CAXXXX.api_surface = private, internal

1. példa

Az alábbi kódtár egy osztály két implementációját mutatja be, amelyek a felhasználói visszajelzésekre adott válaszokat generálják. Az első implementáció (BadRefAndOut) arra kényszeríti a kódtár-felhasználót, hogy három visszatérési értéket kezeljen. A második implementáció (RedesignedRefAndOut) leegyszerűsíti a felhasználói élményt egy tárolóosztály (ReplyData) egy példányának visszaadásával, amely egyetlen egységként kezeli az adatokat.

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
{
    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; }
    public Actions Action { get; }

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

2. példa

Az alábbi alkalmazás a felhasználó élményét mutatja be. Az újratervezett kódtár (UseTheSimplifiedClass metódus) hívása egyszerűbb, és a metódus által visszaadott információk könnyen kezelhetők. A két metódus kimenete azonos.

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 UseClasses()
    {
        UseTheComplicatedClass();

        // Print a blank line in output.
        Console.WriteLine("");

        UseTheSimplifiedClass();
    }
}

3. példa

Az alábbi példatár bemutatja, hogyan ref használják a referenciatípusok paramétereit, és jobban szemlélteti ennek a funkciónak a megvalósítását.

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 += " ABCDE";
    }

    // The following syntax works, 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 += " ABCDE";
    }

    // The following syntax works 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";
    }
}

4. példa

Az alábbi alkalmazás meghívja a kódtár minden metódusát a viselkedés bemutatásához.

public class Test
{
    public static void MainTest()
    {
        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);
    }
}

Ez a példa a következő kimenetet hozza létre:

Changing pointer - passed by value:
12345
12345
Changing pointer - passed by reference:
12345
12345 ABCDE
Passing by return value:
12345 ABCDE

Mintamódszerek kipróbálás

A Try<Something> mintát implementáló metódusok, példáulSystem.Int32.TryParse, nem emelik ki ezt a szabálysértést. Az alábbi példa egy olyan struktúrát (értéktípust) mutat be, amely implementálja a metódust System.Int32.TryParse .

public struct Point
{
    public Point(int axisX, int axisY)
    {
        X = axisX;
        Y = axisY;
    }

    public int X { get; }

    public int Y { get; }

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

CA1045: Ne adjon át típusokat hivatkozás alapján