Pertanyaan Metode tidak murni disebut untuk bidang hanya baca


saya menggunakan Visual Studio 2010 + Resharper dan itu menunjukkan peringatan pada kode berikut:

if (rect.Contains(point))
{
    ...
}

rect adalah readonly Rectangle field, dan Resharper menunjukkan kepada saya peringatan ini:

"Metode Tidak Murni disebut untuk bidang tipe nilai baca."

Apa metode tidak murni dan mengapa peringatan ini ditunjukkan kepada saya?


75
2018-03-29 14:29


asal


Jawaban:


Pertama, jawaban Jon, Michael, dan Jared pada dasarnya benar tetapi saya memiliki beberapa hal lagi yang ingin saya tambahkan kepada mereka.

Apa yang dimaksud dengan metode "tidak murni"?

Lebih mudah untuk mengkarakterisasi metode murni. Metode "murni" memiliki karakteristik sebagai berikut:

  • Outputnya sepenuhnya ditentukan oleh inputnya; outputnya tidak tergantung pada eksternalitas seperti waktu hari atau bit pada hard disk Anda. Outputnya tidak tergantung pada sejarahnya; memanggil metode dengan argumen yang diberikan dua kali harus memberikan hasil yang sama.
  • Metode murni tidak menghasilkan mutasi yang dapat diamati di dunia di sekitarnya. Metode murni dapat memilih untuk bermutasi negara pribadi demi efisiensi, tetapi metode murni tidak, katakanlah, bermutasi bidang argumennya.

Sebagai contoh, Math.Cos adalah metode murni. Outputnya hanya bergantung pada inputnya, dan input tidak diubah oleh panggilan.

Metode yang tidak murni adalah metode yang tidak murni.

Apa sajakah bahaya-bahaya yang melampaui struktur yang hanya bisa dibaca ke metode-metode yang tidak murni?

Ada dua yang muncul dalam pikiran. Yang pertama adalah yang ditunjukkan oleh Jon, Michael dan Jared, dan ini adalah salah satu yang Resharper memperingatkan Anda tentang. Ketika Anda memanggil metode pada struct, kami selalu menyampaikan referensi ke variabel yang merupakan penerima, jika metode ingin mem-mutasi variabel.

Jadi bagaimana jika Anda memanggil metode seperti itu pada nilai, bukan variabel? Dalam hal ini kami membuat variabel sementara, salin nilai ke dalamnya, dan berikan referensi ke variabel.

Variabel yang bisa dibaca dianggap sebagai nilai, karena tidak dapat dimutasi di luar konstruktor. Jadi kita menyalin variabel ke variabel lain, dan metode yang tidak murni mungkin bermutasi salinan, ketika Anda berniat untuk bermutasi variabel.

Itu bahaya melewati sebuah struct yang hanya bisa dibaca sebagai penerima. Ada juga bahaya melewati struct yang berisi bidang hanya baca. Sebuah struct yang berisi bidang hanya baca adalah praktik umum, tetapi pada dasarnya menulis cek bahwa sistem jenis tidak memiliki dana tunai; "read-only-ness" dari variabel tertentu ditentukan oleh pemilik penyimpanan. Contoh dari tipe referensi "memiliki" penyimpanannya sendiri, tetapi instance dari jenis nilai tidak!

struct S
{
  private readonly int x;
  public S(int x) { this.x = x; }
  public void Badness(ref S s)
  {
    Console.WriteLine(this.x);   
    s = new S(this.x + 1);
    // This should be the same, right?
    Console.WriteLine(this.x);   
  }
}

Seseorang berpikir demikian this.x tidak akan berubah karena x adalah bidang hanya baca dan Badness bukan konstruktor. Tapi...

S s = new S(1);
s.Badness(ref s);

... jelas menunjukkan kepalsuan itu. this dan s lihat variabel yang sama, dan bahwa variabel tidak bisa dibaca!


85
2018-03-29 15:32



Metode yang tidak murni adalah metode yang tidak dijamin untuk meninggalkan nilai seperti semula.

Dalam. NET 4 Anda dapat menghias metode dan jenis dengan [Pure] untuk menyatakan mereka murni, dan R # akan memperhatikan hal ini. Sayangnya, Anda tidak dapat menerapkannya ke anggota orang lain, dan Anda tidak dapat meyakinkan R # bahwa suatu jenis / anggota murni dalam proyek .NET 3.5 sejauh yang saya ketahui. (Ini menggigit saya Noda Time sepanjang waktu.)

Itu ide adalah jika Anda memanggil metode yang mengubah variabel, tetapi Anda menyebutnya di bidang hanya-baca, mungkin tidak melakukan apa yang Anda inginkan, jadi R # akan memperingatkan Anda tentang hal ini. Sebagai contoh:

public struct Nasty
{
    public int value;

    public void SetValue()
    {
        value = 10;
    }
}

class Test
{
    static readonly Nasty first;
    static Nasty second;

    static void Main()
    {
        first.SetValue();
        second.SetValue();
        Console.WriteLine(first.value);  // 0
        Console.WriteLine(second.value); // 10
    }
}

Ini akan menjadi peringatan yang sangat berguna jika setiap metode yang benar-benar murni dinyatakan seperti itu. Sayangnya mereka tidak, jadi ada banyak kesalahan positif :(


46
2018-03-29 14:32



Jawaban singkatnya adalah ini adalah false positive, dan Anda dapat dengan aman mengabaikan peringatan.

Jawaban yang lebih panjang adalah bahwa mengakses tipe nilai read-only menciptakan a salinan itu, sehingga setiap perubahan nilai yang dibuat oleh metode hanya akan mempengaruhi salinan. ReSharper tidak menyadarinya Contains adalah metode murni (artinya tidak memiliki efek samping). Eric Lippert berbicara tentang ini di sini: Mutasi Readonly Structs


14
2018-03-29 14:36



Kedengarannya seperti Reshaprer percaya bahwa metode Contains dapat bermutasi rect nilai. Karena rect adalah readonly struct kompiler C # membuat salinan defensif dari nilai untuk mencegah metode dari mutasi a readonly bidang. Pada dasarnya kode final terlihat seperti ini

Rectangle temp = rect;
if (temp.Contains(point)) {
  ...
}

Resharper memperingatkan Anda di sini itu Contains dapat bermutasi rect dengan cara yang akan segera hilang karena itu terjadi sementara.


11
2018-03-29 14:35



Metode yang tidak murni adalah metode yang dapat memiliki efek samping. Dalam hal ini, Resharper tampaknya berpikir itu bisa berubah rect. Mungkin tidak tetapi rantai bukti rusak.


5
2018-03-29 14:33