Anlamlı olan operatör aşırı yüklemesi örnekleri [kapalı]


12

C # öğrenirken, C # operatörün aşırı yüklenmesini desteklediğini buldum. Hangi iyi örnek ile sorun var:

  1. Mantıklı olun (ör. Koyun ve inek adlı sınıf ekleme)
  2. İki dizenin birleştirilmesine örnek değildir

Temel Sınıf Kütüphanesi'nden örnekler açıktır.


10
'Anlam' tanımlayın, lütfen! Cidden, tam da bu noktadaki acı ve ateşli tartışmalar, tam olarak bu konuda büyük bir anlaşmazlık olduğunu göstermektedir. Birçok otorite aşırı yüklü operatörleri reddeder çünkü tamamen beklenmedik şeyler yapmak için yapılabilirler. Diğerleri, yöntem adlarının da tamamen sezgisel olmayan olarak seçilebileceğini söylüyor, ancak adlandırılmış kod bloklarını reddetmek için bir neden yok! Genellikle kesinlikle mantıklı kabul edilen herhangi bir örnek almayacaksınız. Size mantıklı gelen örnekler - belki.
Kilian Foth

@KilianFoth ile tamamen aynı fikirde. Sonuçta derleyen program derleyiciye mantıklı geliyor. Ama ==çarpma yapmak için aşırı yük varsa , benim için mantıklı ama başkaları için mantıklı olmayabilir ! Bu soru, hangi tesis programlama dillerinin meşruiyeti ile mi ilgili? Yoksa en iyi uygulamaları kodlamaktan mı bahsediyoruz?
Dipan Mehta

Yanıtlar:


27

Uygun operatör aşırı yüklemesinin bariz örnekleri, sayıların çalıştığı gibi davranan sınıflardır. Böylece BigInt sınıfları ( Jalayn'ın önerdiği gibi), karmaşık sayılar veya matris sınıfları ( Superbest'in önerdiği gibi), sıradan sayıların matematiksel operatörlerle gerçekten çok iyi eşleştiği işlemlere sahipken, zaman işlemleri ( svick tarafından önerildiği gibi ) bir alt kümeye güzelce eşlenir bu operasyonların.

Gerçekleştirirken Biraz daha soyut, operatörler kullanılabilir gibi set operasyonları yüzden, operator+bir olabilir birlik , operator-bir olabilir tamamlayıcı hangi değil mi' bir operasyon için eklenmesi veya çarpma operatörünü kullanmak, özellikle vb Gerçi paradigma germek için başlıyor t Değişmeli olarak , beklediğiniz gibi.

C # 'ın kendisi, sayısal olmayan operatör aşırı yüklemesinin mükemmel bir örneğine sahiptir . Delege eklemek ve çıkarmak için kullanır +=ve kullanır , yani onları kaydeder ve -=kayıttan çıkarır . Bu işleri iyi çünkü ve çok daha özlü kod onları etmek ve bu sonucu beklediğiniz gibi operatörler çalışma.+=-=

Purist için, string +operatörünün sorunlarından biri, değişmeli olmamasıdır. "a"+"b"ile aynı değildir "b"+"a". Dizeler için bu istisnayı çok yaygın olduğu için anlıyoruz , ancak operator+diğer türlerde kullanmanın değişmeli olup olmayacağını nasıl anlayabiliriz ? Nesne string benzeri olmadığı sürece çoğu insan bunun olduğunu varsayar , ancak insanların ne alacağını asla bilemezsiniz.

Dizelerde olduğu gibi, matrislerin foları da oldukça iyi bilinmektedir. Açıktır Matrix operator* (double, Matrix)ise skalar çarpım olduğu Matrix operator* (Matrix, Matrix)bir olacaktır matris çarpımını , örneğin (örneğin, matris bir nokta ürünün çarpmaların).

Benzer şekilde delegeleri olan operatörlerin kullanımı, matematikten o kadar uzaktır ki, bu hataları yapma ihtimaliniz düşüktür.

Bu arada, en 2011 ACCU toplantısında , Roger Orr & Steve Aşk kaç defa oturum sunulan eşitlik, değer ve kimlik birçok anlamları göz - Bazı nesneler diğerlerinden daha eşittir . Onların slaytlar indirilebilir Richard Harris'in olduğu gibi, kayan nokta eşitlik Ek . Özet: Çok dikkatli ol ile operator==burada, ejderhalar olun!

Operatör aşırı yüklemesi çok güçlü bir semantik tekniktir, ancak aşırı kullanımı kolaydır. İdeal olarak, sadece aşırı yüklenmiş bir operatörün etkisinin ne olduğu bağlamdan çok açık olduğu durumlarda kullanmalısınız. Birçok yönden a.union(b)daha nettir a+bve a*bbir çok daha karanlık daha a.cartesianProduct(b)bir kartezyen çarpımının sonucu bir olurdu, özellikle de SetLike<Tuple<T,T>>bir yerine SetLike<T>.

Operatörün aşırı yüklenmesi ile ilgili gerçek sorunlar, bir programcı bir sınıfın bir şekilde davranacağını varsayar, ancak aslında başka bir şekilde davranır. Bu tür anlamsal çatışma, kaçınmaya çalışmanın önemli olduğunu önerdiğim şeydir.


1
Matrislerdeki operatörlerin gerçekten iyi eşlendiğini söylüyorsunuz, ancak matris çarpımı da değişmeli değil. Ayrıca delegelerdeki operatörler daha da güçlüdür. d1 + d2Aynı türden iki delege için yapabilirsiniz .
svick

1
@ Mark: "Nokta ürün" sadece vektörlerde tanımlanır; iki matrisin çarpılmasına basitçe "matris çarpımı" denir. Ayrım sadece anlamsal olmaktan öte: nokta ürün bir skaler döndürürken, matris çarpma bir matris döndürür (ve bu arada değişmeli değildir) .
BlueRaja - Danny Pflughoeft

26

Kimsenin BCL'deki en ilginç olaylardan birini söylemediğine şaşırdım: DateTimeve TimeSpan. Yapabilirsin:

  • TimeSpanbaşka bir tane almak için iki s ekleyin veya çıkarınTimeSpan
  • TimeSpanolumsuz bir almak için a üzerinde tekli eksi kullanınTimeSpan
  • DateTimealmak için iki s çıkarma birTimeSpan
  • başka TimeSpanbir tane DateTimealmak için a ekle veya çıkartDateTime

Hangi türde bir sürü anlamlı olamaz operatörlerin başka set <, >, <=, >=. BCL'de, örneğin Versionbunları uygular.


Bilgiçlik teorilerinden çok gerçek bir örnek!
SIslam

7

Aklıma gelen ilk örnek, büyük işaretli tamsayılarla çalışmanıza izin veren BigInteger uygulamasıdır . Kaç operatörün aşırı yüklendiğini görmek için MSDN bağlantısını kontrol edin (yani, büyük bir liste var ve tüm operatörlerin aşırı yüklenmiş olup olmadığını kontrol etmedim, ancak kesinlikle öyle görünüyor)

Ayrıca, Java da yaptığım ve Java aşırı yükleme operatörlerine izin vermediğinden, yazmak için inanılmaz derecede tatlı

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

Java'da:

BigDecimal bd = new BigDecimal(0);
bd = bd.add(new BigDecimal(10));

5

Bunu gördüğüme sevindim çünkü Irony ile dalga geçiyorum ve operatörün aşırı yüklenmesi harika. İşte yapabileceklerinin bir örneği .

Bu yüzden Irony bir ".NET Dil Uygulama Kiti" dir ve bir ayrıştırıcı üretecidir (bir LALR ayrıştırıcısı oluşturur). Yacc / lex gibi ayrıştırıcı jeneratörler gibi yeni bir sözdizimi / dil öğrenmek zorunda kalmadan, C # dilinde dilbilgisini operatör aşırı yüküyle yazarsınız. İşte basit bir BNF dilbilgisi

// 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

Bu yüzden basit bir küçük dilbilgisi (sadece BNF öğreniyorum ve dilbilgisi oluşturduğum için tutarsızlıklar varsa lütfen affedin). Şimdi C # 'a bakalım:

  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("+") | "-" | "*" | "/" | "**";

Gördüğünüz gibi, operatörün aşırı yüklenmesi ile dilbilgisini C # 'a yazmak neredeyse tam olarak BNF'de dilbilgisini yazmaktır. Benim için, bu sadece mantıklı değil, aynı zamanda operatör aşırı yüklemesinin büyük bir kullanımıdır.


3

Bunun en önemli örneği operatör == / operatör! =.

İki nesneyi başvuru yerine veri değerleriyle kolayca karşılaştırmak istiyorsanız, .Equals (ve.GetHashCode!) Öğesini aşırı yüklemek ve tutarlılık için! = Ve == işleçlerini yapmak isteyebilirsiniz.

Ben C # rağmen diğer operatörler herhangi bir vahşi aşırı yükler görmedim (ben olsa yararlı olabilir kenar durumlarda olduğunu hayal).


1

MSDN'den alınan bu örnek karmaşık sayıların nasıl uygulanacağını ve normal + işlecini kullanmalarını gösterir.

Başka bir örnek , matris eklemek için nasıl yapılacağını gösterir ve ayrıca bir garaja araba eklemek için nasıl kullanılmayacağını açıklar (bağlantıyı okuyun).


0

Aşırı yükün iyi kullanımı nadir olabilir, ancak olur.

aşırı yükleme operatörü == ve operatör! = iki düşünce okulunu göster: şeyleri kolaylaştırdığını söyleyenler ve adreslerin karşılaştırılmasını önlediğini söyleyenler (yani sadece aynı şeyin bir kopyasını değil, bellekte tam olarak aynı yere işaret ediyorum nesne).

Döküm operatörü aşırı yüklerinin belirli durumlarda kullanışlı olduğunu düşünüyorum. Örneğin, XML'de 0 veya 1 olarak temsil edilen bir booleanı serileştirmek / serileştirmek zorunda kaldım. Sağ (örtülü veya açık, unutmak) boolean'den int ve back'e döküm operatörü hile yaptı.


4
Adreslerin karşılaştırılmasını engellemez: Yine de kullanabilirsiniz object.ReferenceEquals().
dan04

@ dan04 Bilmek çok iyi!
MPelletier

Adresleri karşılaştırmanın başka bir yolu, nesnelerin ==döküm yoluyla kullanımını zorlamaktır : (object)foo == (object)barher zaman referansları karşılaştırır. Ama ReferenceEquals()@ dan04'ten bahsedildiği gibi tercih ederim çünkü ne yaptığı daha açık.
svick

0

İnsanların operatör aşırı yüklenmesi durumunda genellikle düşündükleri şeyler kategorisinde değiller, ancak aşırı yüklenebilecek en önemli operatörlerden birinin dönüşüm operatörü olduğunu düşünüyorum .

Dönüşüm işleçleri, özellikle sayısal bir şekle "şekeri kaldırabilen" veya bazı bağlamlarda sayısal bir tür gibi davranabilen değer türleri için kullanışlıdır. Örneğin, özel bir tanımlayabilir Idbelirli bir temsilcidir türü ve bir sağlayabilir örtülü dönüşüm intbir geçebileceği şekilde Idbir alan için bir yönteme int, ancak explict dönüşüm intiçin Idhiç kimse bir geçebileceği şekilde intbir içine Idilk döküm yapmadan alır .

C # dışında bir örnek olarak, Python dili yüklenebilir işleçler olarak uygulanan birçok özel davranış içerir. Bunlar arasında inüyelik testi ()operatörü, bir nesneyi bir işlevmiş gibi çağırma operatörü ve bir nesnenin lenuzunluğunu veya boyutunu belirleme operatörü bulunur.

Ve sonra Haskell, Scala ve daha pek çok işlevsel dil gibi diller var, bu tür isimler +sadece sıradan işlevler, operatörler değil (ve infix konumunda işlevleri kullanmak için dil desteği var).


0

Nokta Struct içinde System.Drawing ad operatör aşırı yüklenmesini kullanarak iki farklı konuma karşılaştırmak için aşırı kullanır.

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

Gördüğünüz gibi, aşırı yükü kullanarak iki konumun X ve Y koordinatlarını karşılaştırmak çok daha kolay.


0

Eğer matematiksel vektörü biliyorsanız, +operatörün aşırı yüklenmesi için bir kullanım görebilirsiniz . Bir vektör ekleyebilir a=[1,3]ile b=[2,-1]ve almak c=[3,2].

Eşittir (==) 'in aşırı yüklenmesi de yararlı olabilir (muhtemelen bir equals()yöntem uygulamak daha iyidir ). Vektör örneklerine devam etmek için:

v1=[1,3]
v2=[1,3]
v1==v2 // True

-2

Bir forma çizim yapmak için bir kod parçası düşünün

{
  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.
  ..
}

Diğer bir yaygın örnek, bir konumun vektör şeklinde tutulması için bir yapının kullanılmasıdır.

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

sadece daha sonra kullanılacak

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

4
Sen vektörleri, değil pozisyonları ekleyin: \ Bu zaman iyi bir örnektir operator+gerektiğini değil aşırı yüklenmiş (bir vektör açısından bir noktaya uygulamak, ancak iki puan eklemek mümkün olmamalı)
BlueRaja - Dany Pflughoeft

@ BlueRaja-DannyPflughoeft: Başka bir pozisyon vermek için pozisyon eklemek mantıklı değil, ama onları çıkarmak (bir vektör vermek için) ve ortalamaları çıkarmak gibi . Biri p1, p2, p3 ve p4 ortalamasını hesaplayabilir p1+((p2-p1)+(p3-p1)+(p4-p1))/4, ancak bu biraz garip görünüyor.
supercat

1
Afin geometride, ekleme, ölçekleme vb. Gibi noktalar ve çizgilerle cebir yapabilirsiniz. Bununla birlikte, uygulama yine de tipik olarak 3D grafiklerde kullanılan homojen koordinatlar gerektirir. İki noktanın eklenmesi aslında ortalamalarıyla sonuçlanır.
ja72
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.