Перегрузка операторов для реализации класса комплексных чисел



В следующем примере показано, как использовать перегрузку операторов при определении класса комплексных чисел Complex, в котором реализовано комплексное сложение. Для отображения действительной и мнимой частей чисел и результатов сложения в программе используется переопределенная версия метода ToString.

Пример

ß---


Guidelines for Overloading Equals() and Operator ==

In C#, there are two different kinds of equality: reference equality and value equality. Value equality is the generally understood meaning of equality: it means that two objects contain the same values. For example, two integers with the value of 2 have value equality. Reference equality means that there are not two objects to compare. Instead, there are two object references and both of them refer to the same object. This can occur through simple assignment, as shown in the following example:

System.Object a = new System.Object(); System.Object b = a; System.Object.ReferenceEquals(a, b); //returns true

In this code, only one object exists, but there are multiple references to that object: a and b. Because they both refer to the same object, they have reference equality. If two objects have reference equality, they also have value equality, but value equality does not guarantee reference equality.

To check for reference equality, use ReferenceEquals. To check for value equality, use Equals.

Overriding Equals

Because Equals is a virtual method, any class can override its implementation. Any class that represents a value, essentially any value type, or a set of values as a group, such as a complex number class, should override Equals. If the type implements IComparable, it should override Equals.

The new implementation of Equals should follow all the guarantees of Equals:

· x.Equals(x) returns true.

· x. Equals (y) returns the same value as y. Equals (x).

· if (x. Equals (y) && y. Equals (z)) returns true, then x. Equals (z) returns true.

· Successive invocations of x. Equals (y) return the same value as long as the objects referenced by x and y are not modified.

· x. Equals (null) returns false (for non-nullable value types only).

 


Правила переопределения метода Equals и оператора равенства (==)[24]

В языке C# существует два различных типа равенства: равенство ссылок и равенство значений. Равенство значений – это общее понятие равенства: два объекта содержат одинаковые значения. Например, две целых числа со значением 2 обладают равенством значений. Равенство ссылок означает, что для сравнения имеется не два объекта. Вместо них имеет две ссылки на объект, обе из которых связаны с одним объектом. Это достигается путем одного присвоения, как показано в следующем примере:

ß---

В этом коде существует только один объект, но на него имеется несколько ссылок: a и b. Так как обе ссылки связаны с одним и тем же объектом, они обладают равенством ссылок. Если два объекта обладают равенством ссылок, то они также имеют равенство значений, но равенство значений не может гарантировать равенство ссылок.

Для проверки равенства ссылок используется ReferenceEquals. Для проверки равенства значений используется Equals.

Переопределение Equals

Поскольку Equals является виртуальным методом, любой класс может переопределить его реализацию. Любой класс, представляющий значение (в принципе, любой тип значения) или набор значений в качестве группы, такой как класс сложных чисел, должен переопределять Equals. Если тип реализует IComparable, он должен переопределять Equals.

Новая реализация Equals должна соответствовать всем гарантиям Equals:

· x.Equals(x) возвращает true.

· x.Equals (y) возвращает то же значение, что и y.Equals (x).

· если  (x.Equals (y) && y.Equals (z)) возвращает true, то x.Equals (z) возвращает true.

· Последовательные вызовы x.Equals (y) возвращают то же значение до тех пор, пока объекты, на которые ссылается x и y, не будут изменены.

· x.Equals (ноль) возвращает false (только для не нулевых типов значений).


The new implementation of Equals should not throw exceptions. It is recommended that any class that overrides Equals also override Object..::.GetHashCode. It is also recommended that in addition to implementing Equals (object), any class also implement Equals (type) for their own type, to enhance performance. For example:

class TwoDPoint : System.Object { public readonly int x, y; public TwoDPoint(int x, int y) //constructor {    this.x = x;    this.y = y; } public override bool Equals(System.Object obj) {    // If parameter is null return false.    if (obj == null)    {        return false;    }    // If parameter cannot be cast to Point return false.    TwoDPoint p = obj as TwoDPoint;    if ((System.Object)p == null)    {        return false;    }    // Return true if the fields match:    return (x == p.x) && (y == p.y); }   public bool Equals(TwoDPoint p) {    // If parameter is null return false:    if ((object)p == null)    {        return false;    }    // Return true if the fields match:    return (x == p.x) && (y == p.y); }   public override int GetHashCode() {    return x ^ y; } }

Новая реализация Equals не должна создавать исключения. Рекомендуется, чтобы любой класс, переопределяющий Equals, также переопределял Object..::.GetHashCode. Также рекомендуется, чтобы помимо реализации Equals (объект), любой класс также реализовывал Equals (тип) для собственных типов с целью повышения производительности. Пример.

ß---


Any derived class that can call Equals on the base class should do so before finishing its comparison. In the following example, Equals calls the base class Equals, which checks for a null parameter and compares the type of the parameter with the type of the derived class. That leaves the implementation of Equals on the derived class the task of checking the new data field declared on the derived class:

class ThreeDPoint : TwoDPoint { public readonly int z;   public ThreeDPoint(int x, int y, int z)    : base(x, y) {    this.z = z; }   public override bool Equals(System.Object obj) {    // If parameter cannot be cast to ThreeDPoint return false:    ThreeDPoint p = obj as ThreeDPoint;    if ((object)p == null)    {        return false;    }      // Return true if the fields match:    return base.Equals(obj) && z == p.z; }   public bool Equals(ThreeDPoint p) {    // Return true if the fields match:    return base.Equals((TwoDPoint)p) && z == p.z; }   public override int GetHashCode() {    return base.GetHashCode() ^ z; } }

Любой производный класс, который может вызвать Equals в базовом классе, должен вызывать этот метод до завершения сравнения. В следующем примере Equals вызывает базовый класс Equals, который проверяет нулевой параметр и сравнивает тип параметра с типом производного класса. На реализацию Equals в производном классе это накладывает задачу проверки новых полей данных, объявленных в производном классе:

ß---


Overriding Operator ==

By default, the operator == tests for reference equality by determining whether two references indicate the same object. Therefore, reference types do not have to implement operator == in order to gain this functionality. When a type is immutable, that is, the data that is contained in the instance cannot be changed, overloading operator == to compare value equality instead of reference equality can be useful because, as immutable objects, they can be considered the same as long as they have the same value. It is not a good idea to override operator == in non-immutable types.

Overloaded operator == implementations should not throw exceptions. Any type that overloads operator == should also overload operator !=. For example:

//add this code to class ThreeDPoint as defined previously // public static bool operator ==(ThreeDPoint a, ThreeDPoint b) { // If both are null, or both are same instance, return true. if (System.Object.ReferenceEquals(a, b)) {    return true; } // If one is null, but not both, return false. if (((object)a == null) || ((object)b == null)) {    return false; } // Return true if the fields match: return a.x == b.x && a.y == b.y && a.z == b.z; } public static bool operator !=(ThreeDPoint a, ThreeDPoint b) { return !(a == b); }
Note:
A common error in overloads of operator == is to use (a == b), (a == null), or (b == null) to check for reference equality. This instead creates a call to the overloaded operator ==, causing an infinite loop. Use ReferenceEquals or cast the type to Object, to avoid the loop.

Перегрузка оператора равенства (==)

По умолчанию оператор == проверяет равенство ссылок, определяя, указывают ли две ссылки на один и тот же объект. Таким образом, ссылочные типы не должны реализовывать оператор ==, чтобы получить эту функцию. Если тип является неизменяемым, то есть, если содержащиеся в экземпляре данные нельзя изменять, перегрузка оператора == для сравнения равенства значений вместо равенства ссылок может иметь смысл, поскольку как неизменяемые объекты, они могут считаться таковыми до тех пор, пока будут иметь одинаковые значения. Не рекомендуется переопределять оператор == в типах, не являющихся неизменяемыми.

Реализация перегруженного оператора == не должна приводить к исключениям. Любой тип, перегружающий оператор ==, также должен перегружать оператор !=. Пример.

ß---

 

Примечание.
Распространенной ошибкой при перегрузке оператора == является использование (a == b), (a == null) или (b == null) для проверки равенства ссылок. Вместо нужного результата, это создает вызов перегруженного оператора ==, приводя к бесконечному циклу. Чтобы избежать цикла, используйте ReferenceEquals или приведите тип к Object.

 


Objects, Classes and Structs

C# is an object-oriented programming language and uses classes and structs to implement types such as Windows Forms, user interface controls, and data structures. A typical C# application consists of classes defined by the programmer, combined with classes from the .NET Framework.

C# provides many powerful ways of defining classes, such as providing different access levels, inheriting features from other classes, and enabling the programmer to specify what occurs when types are instantiated or destroyed.

Classes can also be defined as generic by using type parameters that enable client code to customize the class in a type-safe and efficient manner. A single generic class, for example System.Collections.Generic..::.List<(Of <(T>)>) in the .NET Framework can be used by client code to store integers, strings, or any other type of object.

Overview

Objects, classes, and structs have the following properties:

· Objects are instances of a given data type. The data type provides a blueprint for the object that is created, or instantiated, when the application is executed.

· New data types are defined by using classes and structs.

· Classes and structs form the building blocks of C# applications that contain code and data. A C# application will always contain of at least one class.

· A struct can be considered a lightweight class, ideal for creating data types that store small amounts of data, and does not represent a type that might later be extended through inheritance.

· C# classes support inheritance; classes can derive from a previously defined class.

 


Объекты, классы и структуры

В языке C# классы (и иногда структуры) используются для определения собственных типов. Пользовательские типы могут содержать любое сочетание встроенных числовых типов, строк, переменных других типов, методов и событий. В большинстве случае классы и структуры определяются непосредственно в пространстве имен, хотя их можно также помещать в другие классы и структуры. После определения типа в виде класса или структуры в коде можно создавать и использовать переменные этого типа. Такие переменные называются экземплярами или объектами (если только класс или структура не были определены как статические).

В следующем примере на верхнем уровне пространства имен ProgrammingGuide определен класс MyCustomClass, содержащий три члена. Экземпляр (объект) класса MyCustomClass создается в методе Main класса Program, а для доступа к методам и свойствам используется точечная нотация.

namespace ProgrammingGuide { // Class definition. public class MyCustomClass {          // Class members:    // Property.    public int Number { get; set; }    // Method.    public int Multiply(int num)    {        return num * Number;    }    // Instance Constructor.    public MyCustomClass()   {        Number = 0;    } } // Another class definition. This one contains // the Main method, the entry point for the program. class Program {    static void Main(string[] args)    {        // Create an object of type MyCustomClass.        MyCustomClass myClass = new MyCustomClass();        // Set the value of a public property.        myClass.Number = 27;        // Call a public method.        int result = myClass.Multiply(4);           } } }

 


 


Общие сведения

Дополнительные сведения о классах см. в разделе Классы. Дополнительные сведения о различиях между структурами и классами и причинах, по которым в большинстве случаев рекомендуется использовать для создания собственных типов классы, а не структуры, см в разделах Структуры и Определение различия между передачей структуры и ссылки класса в метод.

Члены

В следующем списке перечислены все возможные типы членов, которые можно объявлять в классах и (в некоторых случаях) в структурах.

· Константы

· Поля

· Методы

· Свойства

· События

· Индексаторы

· Операторы

· Конструкторы

· Деструкторы

· Вложенные типы

Доступность

Разработчики могут устанавливать уровень доступности типов и их членов для остального кода. Дополнительные сведения см. в разделе Модификаторы доступа.

 

Наследование

Классы (но не структуры) поддерживают наследование. Класс, наследуемый от другого (базового класса), автоматически включает все открытые, защищенные и внутренние члены базового класса, за исключением конструкторов и деструкторов. Дополнительные сведения см. в разделе Наследование.

Классы можно определять как абстрактные (abstract), что будет означать, что один или несколько их членов не имеют реализации. Явным образом создавать экземпляры абстрактных классов невозможно, но эти классы можно использовать в качестве базовых для других классов, в которых недостающие элементы будут реализованы. Классы также могут объявляться запечатанными (sealed), чтобы от них нельзя было наследовать новые классы. Дополнительные сведения см. в разделе Абстрактные и запечатанные классы и члены классов.

Интерфейсы

Классы и структуры способны наследовать различным интерфейсам. Наследование интерфейсу означает, что в этом типе реализованы все методы, определенные в интерфейсе. Дополнительные сведения см. в разделе Интерфейсы.

Универсальные типы

Классы и структуры можно определять с одним или несколькими параметрами типов. Тип указывается в клиентском коде при создании экземпляра данного типа. Например, класс List<(Of <(T>)>) пространства имен System.Collections.Generic определен с одним параметром. При создании экземпляра List<string> или List<int> указывается тип значений, которые можно будет хранить в этом списке. Дополнительные сведения см. в разделе Универсальные шаблоны.

Статические типы

Классы и структуры могут объявляться как статические (static). Статический класс может содержать только статические члены, и с помощью ключевого слова new нельзя будет создать экземпляр такого класса. Одна копия такого класса загружается в память при запуске программы, и все методы этого класса становятся доступными по имени класса. Дополнительные сведения см. в разделе Статические классы и члены статических классов.

Вложенные типы

Определение класса или структуры может быть вложено в определение другого класса или структуры. Дополнительные сведения см. в разделе Вложенные типы.

Разделяемый класс

Существует возможность определить часть класса в одном файле с кодом, а другую часть этого же класса — в другом файле с кодом. Дополнительные сведения см. в разделе Разделяемые классы и методы

Инициализаторы объектов

Создавать экземпляры объектов и коллекций объектов и инициализировать их можно без явного вызова соответствующего конструктора. Дополнительные сведения см. в разделе Инициализаторы объектов и коллекций.

Анонимные типы

В случаях, когда создавать именованный класс неудобно или не требуется, например, при заполнении списка структурами данных, которые не нужно сохранять или передавать другому методу, используются анонимные типы. Дополнительные сведения см. в разделе Анонимные типы.

Методы расширения

Существует возможность расширять класс без создания производного класса. Для этого создается отдельный тип, к методам которого можно обращаться, как если бы они принадлежали исходному типу. Дополнительные сведения см. в разделе Методы расширения.


Дата добавления: 2019-03-09; просмотров: 347; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!