Contoh overloading operator, yang masuk akal [ditutup]

12

Sementara saya belajar C #, saya menemukan bahwa, C # mendukung overloading operator. Saya punya masalah dengan contoh bagus yang:

  1. Masuk akal (mis. Menambahkan kelas bernama domba dan sapi)
  2. Bukan contoh gabungan dari dua string

Contoh dari Perpustakaan Kelas Dasar dipersilakan.

Paweł Sołtysiak
sumber
10
Tolong jelaskan 'sense'! Serius, perdebatan pahit dan berapi-api atas titik ini menunjukkan bahwa ada perbedaan pendapat besar tentang hal ini. Banyak pihak berwenang menolak operator yang kelebihan beban karena mereka dapat dibuat untuk melakukan hal-hal yang sama sekali tidak terduga. Yang lain membalas bahwa nama metode juga dapat dipilih untuk sepenuhnya tidak intuitif, tetapi itu bukan alasan untuk menolak blok kode bernama! Anda hampir pasti tidak akan mendapatkan contoh yang secara umum dianggap masuk akal. Contoh yang tampaknya masuk akal untuk Anda - mungkin.
Kilian Foth
Sepenuhnya setuju dengan @KilianFoth. Akhirnya program yang mengkompilasi, masuk akal untuk dikompilasi. Tetapi jika kelebihan ==melakukan multiplikasi, itu masuk akal bagi saya tetapi mungkin tidak masuk akal bagi orang lain! Apakah pertanyaan ini tentang legitimasi bahasa pemrograman fasilitas apa atau apakah kita berbicara tentang 'pengkodean praktik terbaik'?
Dipan Mehta

Jawaban:

27

Contoh nyata dari overloading operator yang tepat adalah setiap kelas yang berperilaku dengan cara yang sama seperti angka beroperasi. Jadi BigInt kelas (sebagai Jalayn menyarankan), bilangan kompleks atau matriks kelas (sebagai Superbest menyarankan) semua memiliki operasi yang sama yang nomor biasa telah begitu memetakan dengan sangat baik ke operator matematika, sementara waktu operasi (seperti yang disarankan oleh svick ) map baik ke subset dari operasi tersebut.

Sedikit lebih abstrak, operator dapat digunakan ketika melakukan operasi seperti set , sehingga operator+bisa menjadi gabungan , operator-bisa menjadi pelengkap, dll. Ini memang mulai memperluas paradigma, terutama jika Anda menggunakan operator penambahan atau gandakan untuk operasi yang tidak t komutatif , seperti yang Anda harapkan.

C # sendiri memiliki contoh kelebihan operator non-numerik . Ia menggunakan +=dan -=untuk menambah dan mengurangi delegasi , yaitu mendaftar dan tidak mendaftar. Ini bekerja dengan baik karena operator +=dan -=bekerja seperti yang Anda harapkan, dan ini menghasilkan kode yang lebih ringkas.

Untuk purist, salah satu masalah dengan +operator string adalah ia tidak komutatif. "a"+"b"tidak sama dengan "b"+"a". Kami memahami pengecualian untuk string ini karena itu sangat umum, tetapi bagaimana kami bisa tahu apakah menggunakan operator+pada tipe lain akan komutatif atau tidak? Kebanyakan orang akan berasumsi bahwa itu adalah, kecuali objeknya seperti string , tetapi Anda tidak pernah benar-benar tahu apa yang akan diasumsikan orang.

Seperti halnya string, kelemahan matriks juga cukup terkenal. Jelas bahwa itu Matrix operator* (double, Matrix)adalah perkalian skalar, sedangkan Matrix operator* (Matrix, Matrix)akan menjadi perkalian matriks (yaitu matriks perkalian titik-produk) misalnya.

Demikian pula penggunaan operator dengan delegasi jelas sekali jauh dari matematika sehingga Anda tidak mungkin membuat kesalahan itu.

Kebetulan, pada konferensi ACCU 2011 , Roger Orr & Steve Love mempresentasikan sesi tentang beberapa objek yang lebih setara daripada yang lain - lihat banyak makna persamaan, nilai, dan identitas . Slide-slide mereka dapat diunduh , seperti Lampiran Richard Harris tentang kesetaraan floating point . Ringkasan: Berhati-hatilah dengan operator==, ini naga!

Overloading operator adalah teknik semantik yang sangat kuat, tetapi mudah digunakan secara berlebihan. Idealnya Anda hanya boleh menggunakannya dalam situasi ketika sangat jelas dari konteks apa efek dari operator kelebihan beban. Dalam banyak hal a.union(b)lebih jelas daripada a+b, dan a*badalah jauh lebih jelas daripada a.cartesianProduct(b), terutama karena hasil produk Cartesian akan menjadi SetLike<Tuple<T,T>>daripada SetLike<T>.

Masalah sebenarnya dengan overloading operator datang ketika seorang programmer menganggap suatu kelas akan berperilaku dalam satu cara, tetapi sebenarnya berperilaku di lain. Bentrokan semantik semacam ini adalah apa yang saya sarankan penting untuk dihindari.

Mark Booth
sumber
1
Anda mengatakan bahwa operator pada matriks memetakan dengan sangat baik, tetapi perkalian matriks juga tidak komutatif. Operator pada delegasi bahkan lebih kuat. Anda dapat melakukannya d1 + d2untuk dua delegasi dengan tipe yang sama.
svick
1
@ Mark: "dot product" hanya didefinisikan pada vektor; mengalikan dua matriks disebut hanya "perkalian matriks." Perbedaannya lebih dari sekadar semantik: produk titik mengembalikan skalar, sedangkan perkalian-matriks mengembalikan sebuah matriks (dan, omong-omong, non-komutatif) .
BlueRaja - Danny Pflughoeft
26

Saya terkejut tidak ada yang menyebutkan salah satu kasus yang lebih menarik di BCL: DateTimedan TimeSpan. Kamu bisa:

  • tambah atau kurangi dua TimeSpans untuk mendapatkan yang lainTimeSpan
  • gunakan minus unary pada TimeSpanuntuk mendapatkan negasiTimeSpan
  • kurangi dua DateTimes untuk mendapatkanTimeSpan
  • menambah atau mengurangi TimeSpandari DateTimeuntuk mendapatkan yang lainDateTime

Set operator yang bisa masuk akal pada banyak jenis yang <, >, <=, >=. Di BCL, misalnya Versionmengimplementasikannya.

svick
sumber
Contoh yang sangat nyata dan bukan teori pedantic!
SIslam
7

Contoh pertama yang muncul di benak saya adalah implementasi BigInteger , yang memungkinkan Anda untuk bekerja dengan bilangan bulat bertanda besar. Lihat tautan MSDN untuk melihat berapa banyak operator yang kelebihan beban (yaitu, ada daftar besar, dan saya tidak memeriksa apakah semua operator kelebihan beban, tapi sepertinya begitu)

Juga, karena saya juga melakukan Java dan Java tidak memungkinkan operator overload, itu sangat manis untuk menulis

BigInteger bi = new BigInteger(0);
bi += 10;

Daripada di Jawa:

BigDecimal bd = new BigDecimal(0);
bd = bd.add(new BigDecimal(10));
Jalayn
sumber
5

Saya senang melihat ini karena saya telah bermain-main dengan Irony dan memiliki penggunaan operator yang berlebihan. Berikut adalah contoh apa yang dapat dilakukannya.

Jadi Irony adalah ".NET Language Implementation Kit" dan merupakan parser generator (menghasilkan parser LALR). Alih-alih harus mempelajari sintaks / bahasa baru seperti generator parser seperti yacc / lex Anda menulis tata bahasa di C # dengan operator kelebihan. Berikut ini adalah tata bahasa BNF sederhana

// BNF 
Expr := Term | BinExpr
Term := number | ParExpr
ParExpr := "(" + Expr + ")"
BinExpr := number + BinOp + number
BinOp := "+" | "-" | "*" | "/"
number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Jadi ini adalah tata bahasa kecil yang sederhana (permisi jika ada ketidakkonsistenan karena saya baru belajar BNF dan membuat tata bahasa). Sekarang mari kita lihat C #:

  var Expr = new NonTerminal("Expr");
  var Term = new NonTerminal("Term");
  var BinExpr = new NonTerminal("BinExpr");
  var ParExpr = new NonTerminal("ParExpr");
  var BinOp = new NonTerminal("BinOp");
  var Statement = new NonTerminal("Statement");
  var ProgramLine = new NonTerminal("ProgramLine");
  var Program = new NonTerminal("Program", typeof(StatementListNode));
  // BNF Rules - Overloading
  Expr.Rule = Term | BinExpr;
  Term.Rule = number | ParExpr;
  ParExpr.Rule = "(" + Expr + ")";
  BinExpr.Rule = Expr + BinOp + Expr;
  BinOp.Rule = ToTerm("+") | "-" | "*" | "/" | "**";

Seperti yang Anda lihat, dengan operator kelebihan, menulis tata bahasa di C # hampir persis menulis tata bahasa di BNF. Bagi saya, itu tidak hanya masuk akal tetapi juga penggunaan berlebihan operator.

Jetti
sumber
3

Contoh kuncinya adalah operator == / operator! =.

Jika Anda ingin dengan mudah membandingkan dua objek dengan nilai data alih-alih dengan referensi, Anda akan ingin membebani .Equals (and.GetHashCode!), Dan mungkin ingin melakukan operator! = Dan == juga untuk konsistensi.

Saya belum pernah melihat kelebihan liar dari operator lain di C # (saya membayangkan ada kasus tepi di mana mungkin berguna).

Ed James
sumber
1

Contoh dari MSDN ini menunjukkan bagaimana menerapkan bilangan kompleks dan meminta mereka menggunakan operator + normal.

Contoh lain menunjukkan bagaimana melakukannya untuk penambahan matriks, dan juga menjelaskan bagaimana tidak menggunakannya untuk menambahkan mobil ke garasi (baca tautannya).

Hebat
sumber
0

Penggunaan kelebihan muatan yang baik bisa jarang terjadi, tetapi itu memang terjadi.

operator overloading == dan operator! = menunjukkan dua aliran pemikiran: yang untuk mengatakan hal itu membuat segalanya lebih mudah, dan mereka yang menolak mengatakan hal itu mencegah membandingkan alamat (yaitu saya menunjuk ke tempat yang persis sama dalam memori, bukan hanya salinan yang sama obyek).

Saya merasa operator overload berguna dalam situasi tertentu. Sebagai contoh, saya harus membuat serial / deserialize dalam XML boolean direpresentasikan sebagai 0 atau 1. Hak (implisit atau eksplisit, saya lupa) melemparkan operator dari boolean ke int dan kembali melakukan trik.

MPelletier
sumber
4
Itu tidak mencegah membandingkan alamat: Anda masih dapat menggunakan object.ReferenceEquals().
dan04
@ dan04 Sangat bagus untuk tahu!
MPelletier
Cara lain untuk membandingkan alamat adalah dengan memaksa penggunaan objek ==dengan melakukan casting: (object)foo == (object)barselalu membandingkan referensi. Tapi saya lebih suka ReferenceEquals(), seperti @ dan04 menyebutkan karena lebih jelas apa fungsinya.
svick
0

Mereka tidak termasuk dalam kategori hal-hal yang biasanya dipikirkan orang ketika mereka melakukan overloading pada operator, tapi saya pikir salah satu operator yang paling penting untuk dapat overload adalah operator konversi .

Operator konversi sangat berguna untuk tipe nilai yang dapat "mengurangi gula" menjadi tipe numerik, atau dapat bertindak seperti tipe numerik dalam beberapa konteks. Sebagai contoh, Anda mungkin mendefinisikan khusus Idjenis yang mewakili pengenal tertentu, dan Anda bisa memberikan implisit konversi ke intsehingga Anda dapat melewati Idke metode yang mengambil sebuah int, tapi explict konversi dari intke Idsehingga tidak ada yang bisa lulus intmenjadi metode yang membutuhkan Idtanpa casting terlebih dahulu.

Sebagai contoh di luar C #, bahasa Python mencakup banyak perilaku khusus yang diterapkan sebagai operator yang kelebihan beban. Ini termasuk inoperator untuk pengujian keanggotaan, ()operator untuk memanggil objek seolah-olah itu fungsi, dan lenoperator untuk menentukan panjang atau ukuran suatu objek.

Dan kemudian Anda memiliki bahasa seperti Haskell, Scala, dan banyak bahasa fungsional lainnya, di mana nama seperti +hanya fungsi biasa, dan bukan operator sama sekali (dan ada dukungan bahasa untuk menggunakan fungsi dalam posisi infix).

Daniel Pryden
sumber
0

The Titik Struct di System.Drawing namespace menggunakan overloading untuk membandingkan dua lokasi yang berbeda menggunakan operator overloading.

 Point locationA = new Point( 50, 50 );
 Point locationB = new Point( 50, 50 );

 if ( locationA == locationB )
    Console.WriteLine( "Their locations are the same" );
 else
    Console.WriteLine( "Their locations  are different" );

Seperti yang Anda lihat, jauh lebih mudah untuk membandingkan koordinat X dan Y dari dua lokasi menggunakan kelebihan beban.

Karthik Sreenivasan
sumber
0

Jika Anda terbiasa dengan vektor matematika Anda mungkin melihat kegunaan dalam membebani +operator. Anda dapat menambahkan vektor a=[1,3]dengan b=[2,-1]dan dapatkan c=[3,2].

Kelebihan yang sama dengan (==) juga bisa menjadi berguna (meskipun mungkin lebih baik untuk menerapkan equals()metode). Untuk melanjutkan contoh-contoh vektor:

v1=[1,3]
v2=[1,3]
v1==v2 // True
MartinHaTh
sumber
-2

Bayangkan sepotong kode untuk menggambar pada formulir

{
  Point p = textBox1.Location;
  Size dp = textBox1.Size;

  // Here the + operator has been overloaded by the CLR
  p += dp;  // Now p points to the lower right corner of the textbox.
  ..
}

Contoh umum lainnya adalah ketika struktur digunakan untuk menyimpan informasi posisi dalam bentuk vektor.

public struct Pos
{
    public double x, y, z;
    public double Distance { get { return Math.Sqrt(x * x + y * y + z * z); } }
    public static Pos operator +(Pos A, Pos B)
    {
        return new Pos() { x = A.x + B.x, y = A.y + B.y, z = A.z + B.z };
    }
    public static Pos operator -(Pos A, Pos B)
    {
        return new Pos() { x = A.x - B.x, y = A.y - B.y, z = A.z - B.z };
    }
}

hanya untuk digunakan nanti sebagai

{
    Pos A = new Pos() { x = 4, y = -1, z = 0.5 };
    Pos B = new Pos() { x = 8, y = 2, z = 1.5 };

    double x = (B - A).Distance;
}
Ja72
sumber
4
Anda menambahkan vektor, bukan posisi: \ Ini adalah contoh yang baik dari saat operator+seharusnya tidak kelebihan beban (Anda dapat menerapkan titik dalam hal vektor, tetapi Anda tidak harus mampu menambah dua poin)
BlueRaja - Danny Pflughoeft
@ BlueRaja-DannyPflughoeft: Menambahkan posisi untuk menghasilkan posisi lain tidak masuk akal, tetapi mengurangkannya (untuk menghasilkan vektor) tidak, seperti halnya rata - rata mereka. Seseorang dapat menghitung rata-rata p1, p2, p3, dan p4 via p1+((p2-p1)+(p3-p1)+(p4-p1))/4, tetapi itu tampaknya agak canggung.
supercat
1
Dalam geometri affine Anda dapat melakukan aljabar dengan titik dan garis, seperti penjumlahan, penskalaan, dll. Implementasinya membutuhkan koordinat yang homogen, yang biasanya digunakan dalam grafik 3D. Penambahan dua poin sebenarnya menghasilkan rata-rata mereka.
ja72