Bagikan melalui


CA1021: Hindari parameter keluar

Properti Nilai
ID Aturan CA1021
Judul Hindari parameter
Golongan Desain
Perbaikan bersifat disruptif atau non-disruptif Merusak
Diaktifkan secara default di .NET 8 Tidak

Penyebab

Metode publik atau terlindungi dalam jenis publik memiliki out parameter .

Secara default, aturan ini hanya melihat jenis yang terlihat secara eksternal, tetapi ini dapat dikonfigurasi.

Deskripsi aturan

Meneruskan jenis berdasarkan referensi (menggunakan out atau ref) memerlukan pengalaman dengan penunjuk, memahami bagaimana jenis nilai dan jenis referensi berbeda, dan menangani metode dengan beberapa nilai pengembalian. Selain itu, perbedaan antara parameter out dan ref tidak dipahami secara luas.

Ketika jenis referensi diteruskan "berdasarkan referensi," metode berniat menggunakan parameter untuk mengembalikan instans objek yang berbeda. Meneruskan jenis referensi menurut referensi juga dikenal sebagai menggunakan penunjuk ganda, penunjuk ke penunjuk, atau tidak langsung ganda. Dengan menggunakan konvensi panggilan default, yang melewati "menurut nilai," parameter yang mengambil jenis referensi sudah menerima penunjuk ke objek. Pointer, bukan objek yang ditunjuknya, diteruskan berdasarkan nilai. Meneruskan menurut nilai berarti bahwa metode tidak dapat mengubah penunjuk agar menunjuk ke instans baru dari jenis referensi. Namun, ini dapat mengubah konten objek yang ditikamnya. Untuk sebagian besar aplikasi, ini cukup dan menghasilkan perilaku yang diinginkan.

Jika suatu metode harus mengembalikan instans yang berbeda, gunakan nilai pengembalian metode untuk mencapai ini. Lihat kelas System.String untuk berbagai metode yang beroperasi pada string dan mengembalikan instans baru string. Ketika model ini digunakan, pemanggil harus memutuskan apakah objek asli dipertahankan.

Meskipun nilai pengembalian adalah hal yang umum dan banyak digunakan, penerapan parameter out dan ref yang benar membutuhkan keterampilan desain dan pengodean menengah. Arsitek pustaka yang merancang untuk audiens umum seharusnya tidak mengharapkan pengguna menjadi mahir dalam bekerja dengan parameter out atau ref.

Cara memperbaiki pelanggaran

Untuk memperbaiki pelanggaran aturan ini yang disebabkan oleh jenis nilai, minta metode mengembalikan objek sebagai nilai pengembaliannya. Jika metode harus mengembalikan beberapa nilai, desain ulang untuk mengembalikan satu instans objek yang menyimpan nilai.

Untuk memperbaiki pelanggaran aturan ini yang disebabkan oleh jenis referensi, pastikan bahwa perilaku yang diinginkan adalah mengembalikan instans referensi baru. Jika demikian, metode harus menggunakan nilai pengembaliannya untuk melakukan ini.

Kapan harus menekan peringatan

Aman untuk menyembunyikan peringatan dari aturan ini. Namun, desain ini dapat menyebabkan masalah kegunaan.

Menyembunyikan peringatan

Jika Anda hanya ingin menyembunyikan satu pelanggaran, tambahkan arahan praprosedur ke file sumber Anda untuk dinonaktifkan lalu aktifkan kembali aturannya.

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

Untuk menonaktifkan aturan untuk file, folder, atau proyek, atur tingkat keparahannya ke none dalam file konfigurasi.

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

Untuk informasi selengkapnya, lihat Cara menyembunyikan peringatan analisis kode.

Mengonfigurasi kode yang akan dianalisis

Gunakan opsi berikut untuk mengonfigurasi bagian mana dari codebase Anda yang akan menjalankan aturan ini.

Anda dapat mengonfigurasi opsi ini hanya untuk aturan ini, untuk semua aturan yang berlaku untuknya, atau untuk semua aturan dalam kategori ini (Desain) yang berlaku untuk aturan ini. Untuk informasi selengkapnya, lihat Opsi konfigurasi aturan kualitas kode.

Menyertakan permukaan API tertentu

Anda dapat mengonfigurasi bagian mana dari basis kode yang akan menjalankan aturan ini, berdasarkan aksesibilitasnya. Misalnya, untuk menentukan bahwa aturan hanya boleh dijalankan pada permukaan API non-publik, tambahkan pasangan kunci-nilai berikut ke file .editorconfig di proyek Anda:

dotnet_code_quality.CAXXXX.api_surface = private, internal

Contoh 1

Pustaka berikut menunjukkan dua implementasi kelas yang menghasilkan respons terhadap umpan balik pengguna. Implementasi pertama (BadRefAndOut) memaksa pengguna pustaka untuk mengelola tiga nilai pengembalian. Implementasi kedua (RedesignedRefAndOut) menyederhanakan pengalaman pengguna dengan mengembalikan instans kelas kontainer (ReplyData) yang mengelola data sebagai unit tunggal.

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

Contoh 2

Aplikasi berikut mengilustrasikan pengalaman pengguna. Panggilan ke pustaka (UseTheSimplifiedClass metode) yang didesain ulang lebih mudah, dan informasi yang dikembalikan oleh metode ini mudah dikelola. Output dari kedua metode identik.

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

Contoh 3

Contoh pustaka berikut menggambarkan bagaimana ref parameter untuk jenis referensi digunakan dan menunjukkan cara yang lebih baik untuk menerapkan fungsionalitas ini.

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

Contoh 4

Aplikasi berikut memanggil setiap metode di pustaka untuk menunjukkan perilaku.

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

Contoh ini menghasilkan output berikut:

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

Mencoba metode pola

Metode yang mengimplementasikan pola Coba<Sesuatu> , seperti System.Int32.TryParse, jangan memunculkan pelanggaran ini. Contoh berikut menunjukkan struktur (jenis nilai) yang mengimplementasikan System.Int32.TryParse metode .

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: Jangan teruskan jenis dengan referensi