Share via


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

Tulajdonság Érték
Szabályazonosító CA1045
Cím Típusok átadása hivatkozás alapján
Kategória Design
A javítás kompatibilitástörő vagy nem törik Törés
Alapértelmezés szerint engedélyezve a .NET 8-ban Nem

Ok

A nyilvános vagy védett metódus olyan paraméterrel rendelkezik ref , amely primitív típust, referenciatípust vagy olyan értéktípust használ, amely nem tartozik a beépített típusok közé.

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ó mutatóra mutató mutató vagy dupla közvetettség használatával is ismert.) Az alapértelmezett hívási konvencióval, amely "érték szerint" ad át, 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 továbbítá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, de 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álatával a hívó dönti el, 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.

Megjegyzés:

Ha nagy struktúrákat tartalmazó paraméterekkel dolgozik, a struktúrák másolásához szükséges további erőforrások teljesítményhatást okozhatnak, ha értéket ad át. Ezekben az esetekben érdemes lehet használni vagy out paramétereket használniref.

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ályból származó figyelmeztetéseket nyugodtan el lehet tiltani; azonban ez a kialakítás 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 CA1045
// The code that's violating the rule is on this line.
#pragma warning restore CA1045

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.CA1045.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

Example 1

Az alábbi kódtár egy osztály két olyan implementációját mutatja be, amelyek válaszokat hoznak létre a felhasználó visszajelzésére. 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
{
    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;
    }
}

Example 2

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 Main1045()
    {
        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 a funkció implementálásának jobb módját mutatja be.

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

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 Main1045()
    {
        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

CA1021: A paraméterek elkerülése