Здравствуйте, Аноним, Вы писали:
А>Вопрос в том почему, для одних и тех-же значения вызывается в одном случае вызывается Point.operator==, а в другом object.operator== !!! Почему для метода с параметром типа, не вызывается Point.operator==
1. Переопределение оператора== — это возможность языка C# и чтобы он его применял, должно быть статически известно что слева и справа оператора == будут значения типа Point. В приведённом generic-коде такого знания нет, так как сравниваются два значения типа T, а не Point.
2. Проверка эквивалентности в .NET — довольно щепетильная тема, одного переопределения операторов в C# недостаточно. Стоит хотя бы обратить внимание на warning'и, которыми ругается компилятор на вышеприведённый код Если вкратце, то переопределяя операторы ==/!= следует так же переопределить метод object.Equals(object) и object.GetHashCode(), при этом желательно чтобы реализация .Equals() полагалась на реализацию оператора== (хотя это можно оспорить ), а так же возвращала false если входной аргумент является null или значением типа, не являющимся Point. Более того, если Point — структура, то следует реализовать интерфейс IEquatable<T>.
3. В generic-коде всё хитро Оператор== применять к аргументам типа T возможно только если на тип-параметр T наложено ограничение where T: class, при этом сравнение будет простым ссылочным сравнением, аналогично Object.ReferenceEquals(). Обобщённому коду совершенно неизвестно, что в подставляемых типах могут быть переопределены какие-то операторы и всё, что он может сделать — сравнивать ссылочно, так как из ограничения на тип-параметр известно, что сравниваться будут управляемые ссылки.
4. Чтобы правильно сравнивать или проверять эквивалентность значений в generic-коде следует воспользоваться экземплярами компараторов, возвращаемых свойствами EqualityComparer<T>.Default (для проверки эквивалентности) и Comparer<T>.Default (для сравнения).
5. Ваш код может выглядеть как-то так:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public static bool operator ==(Point a, Point b)
{
return ((object) a != null) && ((object) b != null) && (a.X == b.X) && (a.Y == b.Y);
}
public static bool operator !=(Point op1, Point op2)
{
return !(op1 == op2);
}
public override bool Equals(object obj)
{
var p = obj as Point;
return (p != null) && (this == p);
}
public override int GetHashCode() { return X ^ Y; }
}
public class Test
{
public static void Main()
{
Point A = new Point();
A.X = 10;
A.Y = 11;
Point copyA = new Point();
copyA.X = 10;
copyA.Y = 11;
Point B = new Point();
B.X = 15;
B.Y = 12;
Console.WriteLine((A == copyA).ToString().ToUpper()); //true - используется Point.operator==
Console.WriteLine((A == B).ToString().ToUpper()); //false - используется Point.operator==
GenericTest<Point>(A, copyA, B);
}
static void GenericTest<T>(T A, T copyA, T B)
{
var comparer = EqualityComparer<T>.Default;
Console.WriteLine((comparer.Equals(A, copyA)).ToString().ToUpper()); //true
Console.WriteLine((comparer.Equals(A, B)).ToString().ToUpper()); //false
}
static void NonGenericTest(Point A, Point copyA, Point B)
{
Console.WriteLine((A == copyA).ToString().ToUpper()); //true - используется Point.operator==
Console.WriteLine((A == B).ToString().ToUpper()); //false - используется Point.operator==
}
}
Re[3]: Не работает сравнение в Generics-методе ???
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, Пельмешко, Вы писали:
П>>…Более того, если Point — структура, то следует реализовать интерфейс IEquatable<T>.
_FR>А в классах, значит, IEquatable<T> не нужен?
Я так и знал, что кто-нибудь докопается
Писал ответ в пол второго после почти 12 часового рабочего дня, решил что дорогому Анониму не обязательно знать, что IEquatable<T> следует так же реализовывать в неизменяемых ref-типах с семантикой значений (System.String и System.Version как пример).
Упомянул структуры, так как считаю основным бенефитом IEquatable<T> отсутствие боксинга обоих сравниваемых операндов value-типа при сравнении через EqualityComparer<T>.Default, а то что он ликвидирует проверку типа и downcast, требуемые в object.Equals(object) — это уже не так важно имхо
Re[4]: Не работает сравнение в Generics-методе ???
Здравствуйте, Пельмешко, Вы писали:
П>>>…Более того, если Point — структура, то следует реализовать интерфейс IEquatable<T>. _FR>>А в классах, значит, IEquatable<T> не нужен? П>Я так и знал, что кто-нибудь докопается
П>Писал ответ в пол второго после почти 12 часового рабочего дня, решил что дорогому Анониму не обязательно знать, что IEquatable<T> следует так же реализовывать в неизменяемых ref-типах с семантикой значений (System.String и System.Version как пример).
Угу.
П>Упомянул структуры, так как считаю основным бенефитом IEquatable<T> отсутствие боксинга обоих сравниваемых операндов value-типа при сравнении через EqualityComparer<T>.Default, а то что он ликвидирует проверку типа и downcast, требуемые в object.Equals(object) — это уже не так важно имхо
Совершенно точно! Конечно, для классов IEquatable<> не играет такой роли, как для структур, но реализовывает его там всё равно нужно.
Help will always be given at Hogwarts to those who ask for it.
Не работает сравнение в Generics-методе ???
От:
Аноним
Дата:
20.07.10 20:24
Оценка:
Такой вот код:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public static bool operator ==(Point op1, Point op2)
{
Console.WriteLine("Point.operator==");
return op1.X == op2.X && op1.Y == op2.Y;
}
public static bool operator !=(Point op1, Point op2)
{
return !(op1 == op2);
}
}
public class Test
{
public void Main()
{
Point A = new Point();
A.X = 10;
A.Y = 11;
Point copyA = new Point();
copyA.X = 10;
copyA.Y = 11;
Point B = new Point();
B.X = 15;
B.Y = 12;
Console.WriteLine((A == copyA).ToString().ToUpper()); //true - используется Point.operator==
Console.WriteLine((A == B).ToString().ToUpper()); //false - используется Point.operator==
GenericTest<Point>(A, copyA, B);
}
static void GenericTest<T>(T A, T copyA, T B)
where T : class
{
Console.WriteLine((A == copyA).ToString().ToUpper()); //false
Console.WriteLine((A == B).ToString().ToUpper()); //false
}
static void NonGenericTest(Point A, Point copyA, Point B)
{
Console.WriteLine((A == copyA).ToString().ToUpper()); //true - используется Point.operator==
Console.WriteLine((A == B).ToString().ToUpper()); //false - используется Point.operator==
}
}
Вопрос в том почему, для одних и тех-же значения вызывается в одном случае вызывается Point.operator==, а в другом object.operator== !!! Почему для метода с параметром типа, не вызывается Point.operator==
Re[2]: Не работает сравнение в Generics-методе ???
Здравствуйте, Пельмешко, Вы писали:
П>…Более того, если Point — структура, то следует реализовать интерфейс IEquatable<T>.
А в классах, значит, IEquatable<T> не нужен?
П>4. Чтобы правильно сравнивать или проверять эквивалентность значений в generic-коде следует воспользоваться экземплярами компараторов, возвращаемых свойствами EqualityComparer<T>.Default (для проверки эквивалентности) и Comparer<T>.Default (для сравнения).
Ещё лучше: добавить перегрузку, принимающую компаратор.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Аноним, Вы писали:
А>Вопрос в том почему, для одних и тех-же значения вызывается в одном случае вызывается Point.operator==, а в другом object.operator== !!! Почему для метода с параметром типа, не вызывается Point.operator==
В C# обычно разрешение перегрузок для операторов происходит статически, на основе compile-time типов. Если нужна диспетчеризация на основе runtime-типов объектов, то надо пользоваться виртуальными методами или реализацией интерфейсов. При использовании generics придётся указать соответствующий базовый класс или интерфейс в констрейнтах у типа-параметра.
Но из этих правил есть одно исключение. Разрешение большинства операторов можно перенести в runtime, если тип одного из операндов dynamic. Но за это придется заплатить некоторую цену. Во-первых, это влияет на производительность (несмотря на то, что в DLR применяется хитроумное кеширование). Во-вторых, во время компиляции отключается любой контроль за наличием у типа соответствующих операторов подходящего типа (если что-то пойдет не так, будет исключение в runtime).
static void GenericTest<T>(T A, T copyA, T B)
where T : class
{
Console.WriteLine((A == (dynamic)copyA).ToString().ToUpper()); //false
Console.WriteLine((A == (dynamic)B).ToString().ToUpper()); //false
}
Re[2]: Не работает сравнение в Generics-методе ???
От:
Аноним
Дата:
22.07.10 11:26
Оценка:
Здравствуйте, nikov, Вы писали:
N>Разрешение большинства операторов можно перенести в runtime, если тип одного из операндов dynamic. Но за это придется заплатить некоторую цену. Во-первых, это влияет на производительность (несмотря на то, что в DLR применяется хитроумное кеширование). Во-вторых, во время компиляции отключается любой контроль за наличием у типа соответствующих операторов подходящего типа (если что-то пойдет не так, будет исключение в runtime).
N>
N>static void GenericTest<T>(T A, T copyA, T B)
N> where T : class
N>{
N> Console.WriteLine((A == (dynamic)copyA).ToString().ToUpper()); //false
N> Console.WriteLine((A == (dynamic)B).ToString().ToUpper()); //false
N>}
N>
А почему бы не сделать так:
static void GenericTest(dynamic A, dynamic copyA, dynamic B)
{
Console.WriteLine((A == copyA).ToString().ToUpper());
Console.WriteLine((A == B).ToString().ToUpper());
}
Если не забыть про возможность получить исключение в runtime, что измениться
Re[3]: Не работает сравнение в Generics-методе ???
Здравствуйте, Аноним, Вы писали:
А>А почему бы не сделать так: А>
А>static void GenericTest(dynamic A, dynamic copyA, dynamic B)
А>{
А> Console.WriteLine((A == copyA).ToString().ToUpper());
А> Console.WriteLine((A == B).ToString().ToUpper());
А>}
А>
Можно и так.
Re[4]: Не работает сравнение в Generics-методе ???
От:
Аноним
Дата:
23.07.10 11:43
Оценка:
Здравствуйте, nikov, Вы писали:
N>Здравствуйте, Аноним, Вы писали:
А>>А почему бы не сделать так: А>>
А>>static void GenericTest(dynamic A, dynamic copyA, dynamic B)
А>>{
А>> Console.WriteLine((A == copyA).ToString().ToUpper());
А>> Console.WriteLine((A == B).ToString().ToUpper());
А>>}
А>>
N>Можно и так.
Подводя итог, можно-ли сказать, что dynamic это то-же, что и var, только при использовании var разрешение происходит статически, во время компиляции, а при использовании dynamic — динамически, во время выполнения. Причем алгоритмы разрешения одни и те-же, разница только в том когда они применяются