Предефиниране на оператори в C#

Въведение

Операторите служат за групиране на различни операнди в изрази. Различните оператори имат различна семантика, в зависимост от типа, с който работят. Например операторът +, ако се използва с числа, означава "събиране", а ако се използва с низове - "конкатенация".

Всеки оператор има определена асоциативност, която определя в каква посока ще се извършат изчисленията. Например, операторът + е с лява асоциативост. Това означава, че операцията a+b е еквивалентна на към a прибавяме b. Операторът = е с дясна асоциативност. За това операцията a=b=5 е еквивалентна на b=5, след което a=b.

Всеки оператор си има и собствен приоритет. Например изразът a-b*c е еквивалентен на a-(b*c).

Някои ограничения

Въпреки възможността, която ни предоставя езикът C# да предефинираме оператори, има някои съвсем смислени ограничения в тази насока:

  • НЕ можете да променяте асоциативността на операторите
  • НЕ можете да създавате нови оператори
  • НЕ можете да променяте значението на операторите (например: 1+1=2 и това не можете да го промените)
  • НЕ можете да предефинирате някои оператори (като точката, която служи за достъп до член на обекта)

Самото предефиниране

Малко "суха" материа

Предефинирането на оператори изглежда така:

  1. public static [Class] operator[Operator] ([param1 [, param2]])
  • операторът е публичен
  • операторът е статичен
  • бинарните оператори (пр. +) притежават два аргумента, а унарните (пр. ++) - само един

Същността на темата

Нека имаме следния клас.

  1. class MyClass
  2. {
  3.  private int myValue;
  4.  
  5.  public int MyValue
  6.  {
  7.   get { return this.myValue; }
  8.   set { this.myValue = value; }
  9.  }
  10.  
  11.  public MyClass (int value)
  12.  {
  13.   this.MyValue = value;
  14.  }
  15. }

Искаме да можем да извършваме операциите събиране, изваждане, умножение и деление с обекти от този тип. За тази цел ще трябва да си предефинираме тези оператори. Ето един пример, който илюстрира как стават нещата на практика.

  1. public static MyClass operator+ (MyClass lObj, MyClass rObj)
  2. {
  3.  return new MyClass (lObj.MyValue + rObj.MyValue);
  4. }

В случая указахме, че когато събираме два обекта от тип MyClass, искаме всъщност да събираме стойностите на техните полета myValue. По аналогичен начин, трябва да предефинираме и операторите за изваждане, умножение и деление.

Много е удобно (и е честа практика), когато предефинираме бинарни оператори, единия аргумент да го кръщаваме lObj (от left - ляв), а другия съответно rObj (от right - десен). Така е по-лесно да знаем с кой от двата обекта работим.

Сега обаче искаме да можем да извършваме действия не само с обекти от нашия тип. Например искаме да можем да събираме обект от нашия тип с цяло число. Ето и решението на този проблем.

  1. public static MyClass operator+ (MyClass lObj, int rObj)
  2. {
  3.  return (lObj + new MyClass(rObj));
  4. }

Забележете, че когато събирам обект от тип MyClass и цяло число, всъщност събираме два обекта от тип MyClass. Това се прави с цел, запазването на главната логика за събирането на обекти от нашия тип. Въпреки, че в случая нашето събиране се свежда до много проста операция, в много други случаи ще се изисква нещо много по-сложно. И тогава ще се забележи предимството на този метод за запазване на главната логика.

Има един "проблем" - както сме предефинирали в момента оператора за събиране, можем да събираме само обекти от тип MyClass с целия числа (или с други обекти от същия тип). Но, ако се опитаме да събереме цяло число с обект от тип MyClass, ще възникне грешка. Разбира се има решение на този проблем. Ето го и него.

  1. public static MyClass operator+ (int lObj, MyClass rObj)
  2. {
  3.  return (new MyClass(lObj) + rObj);
  4. }

Искам да спомена само, че изразът a += b е еквивалентен на a = a + b.

Остана само да покажа, как става предефинирането на унарни оператори (++ / --). Следния пример напълно ще изясни същността на въпроса.

  1. public static MyClass operator++ (MyClass obj)
  2. {
  3.  return (obj + new MyClass(1));
  4. }

Заключение

Мисля, че с написаното до тук, съм ви дал една представа за това, как става предефиниране на оператори в C#. Ако имате неясноти по въпроса, можете да се обърнете към мен.

Коментари

Към тази статия все още няма коментари. Бъдете първи и напишете вашия коментар.

обратно към всички статии