Bagikan melalui


CA1045: Jangan teruskan jenis berdasarkan referensi

Properti Nilai
ID Aturan CA1045
Judul Jangan meneruskan jenis berdasarkan referensi
Golongan Desain
Perbaikan bersifat disruptif atau non-disruptif Merusak
Diaktifkan secara default di .NET 8 Tidak

Penyebab

Metode publik atau terlindungi pada jenis publik memiliki parameter ref yang mengambil jenis primitif, jenis referensi, atau jenis nilai yang bukan salah satu jenis bawaan.

Deskripsi aturan

Meneruskan jenis dengan referensi (menggunakan out atau ref) memerlukan pengalaman menggunakan pointer, memahami bagaimana jenis nilai dan jenis referensi berbeda, dan menangani metode yang memiliki 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 penggunaan pointer ganda, pointer ke pointer, atau indirection ganda.) Penggunaan konvensi pemanggilan default, yang diteruskan "menurut nilai," parameter yang mengambil jenis referensi sudah menerima pointer ke objek. Pointer, bukan objek yang ditunjuknya, diteruskan berdasarkan nilai. Meneruskan berdasarkan nilai berarti bahwa metode tidak dapat mengubah pointer agar merujuk ke instans baru dari jenis referensi, tetapi dapat mengubah konten objek yang dirujuknya. Untuk sebagian besar aplikasi, ini cukup dan menghasilkan perilaku yang Anda inginkan.

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. Dengan menggunakan model ini, 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.

Catatan

Saat Anda menggunakan parameter yang merupakan struktur besar, sumber daya tambahan yang diperlukan untuk menyalin struktur ini dapat menyebabkan efek performa saat Anda meneruskan berdasarkan nilai. Dalam kasus ini, Anda mungkin mempertimbangkan penggunaan parameter ref atau out.

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 Anda inginkan adalah mengembalikan instans referensi yang 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 CA1045
// The code that's violating the rule is on this line.
#pragma warning restore CA1045

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

[*.{cs,vb}]
dotnet_diagnostic.CA1045.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
{
    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;
    }
}

Contoh 2

Aplikasi berikut mengilustrasikan pengalaman pengguna. Panggilan ke pustaka yang didesain ulang (metode UseTheSimplifiedClass) lebih simpel, dan informasi yang ditampilkan 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 Main1045()
    {
        UseTheComplicatedClass();

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

        UseTheSimplifiedClass();
    }
}

Contoh 3

Contoh pustaka berikut mengilustrasikan cara parameter ref untuk jenis referensi digunakan, dan menunjukkan cara yang lebih baik untuk mengimplementasikan 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 = 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";
    }
}

Contoh 4

Aplikasi berikut memanggil setiap metode di pustaka untuk menunjukkan perilaku.

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

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

CA1021: Hindari parameter keluar