В прошлый раз была предложена реализация сравнения объектов на равенство, для унификации которого служат методы типа Object и интерфейс IEquatable<>. Теперь рассмотрим самый простой способ реализовать сравнение объектов на "больше-меньше". Ниже снова будет не только множество скучного кода и ещё более скучных пояснений, но и сниппеты для упрощения его, кода, использования.
Но для начала ещё раз вспомним цель: получить максимально простую, шаблонную реализацию, с минимумом дублирования кода. Вся логика будет в одном единственном методе. Все остальные методы, прямо или косвенно, будут приводить к нему. А методы будут следующие: реализация (явная) IComparable, реализация IComparable<>, а так же определение операторов <, >, <= и >=. Как и в прошлый раз, рассмотрим типы:
using System;
internal struct MyValue
{
public MyValue(int first, int? second) : this() {
First = first;
Second = second;
}
public int First { get; private set; }
public int? Second { get; private set; }
}
internal sealed class MyData
{
public MyData(string name, MyValue value) {
Name = name ?? String.Empty;
Value = value;
}
public string Name { get; private set; }
public MyValue Value { get; private set; }
}
using System;
using System.Collections.Generic;
internal struct MyValue : IComparable, IComparable<MyValue>
{
public MyValue(int first, int? second)
: this() {
First = first;
Second = second;
}
public int First { get; private set; }
public int? Second { get; private set; }
#region IComparable Members
int IComparable.CompareTo(object obj) {
if(!(obj is MyValue)) {
throw new ArgumentException("Argument is not the same type as this instance.", "obj");
}//if
return CompareTo((MyValue)obj);
}
#endregion IComparable Members
#region IComparable<MyValue> Members
public int CompareTo(MyValue other) {
var compare = Comparer<int>.Default.Compare(First, other.First);
if(compare == 0) {
compare = Comparer<int?>.Default.Compare(Second, other.Second);
}//if
return compare;
}
#endregion IComparable<MyValue> Members
public static bool operator <(MyValue left, MyValue right) {
return left.CompareTo(right) < 0;
}
public static bool operator >(MyValue left, MyValue right) {
return left.CompareTo(right) > 0;
}
public static bool operator <=(MyValue left, MyValue right) {
return !(left > right);
}
public static bool operator >=(MyValue left, MyValue right) {
return !(left < right);
}
}
Особенности:
- IComparable.CompareTo(object) первым делом проверяет тип аргумента, и если он не эквивалентен типу текущего экземпляра, выбрасывает исключение. Таков контракт этого метода в интерфейсе.
- CompareTo(MyValue) попарно сравнивает поля текущего экземпляра с переданным образцом до тех пор, пока одно из сравнений не даст не-нулевой результат. Это и будет результатом сравнения текущего экземпляра и образца.
- Операторы < и > сравнивают результат вызова CompareTo(MyValue) с нулём, а нестрогие операторы <= и >= реализованы посредством строгих.
- Пространство имён System.Collections.Generic подключено из-за использования класса Comparer<>.
Реализация для ссылочных типов:
using System;
using System.Collections.Generic;
internal sealed class MyData : IComparable, IComparable<MyData>
{
public MyData(string name, MyValue value) {
Name = name ?? String.Empty;
Value = value;
}
public string Name { get; private set; }
public MyValue Value { get; private set; }
#region IComparable Members
int IComparable.CompareTo(object obj) {
var other = obj as MyData;
if(other == null) {
if(obj == null) {
return 1; // Some value, that greater then zero.
} else {
throw new ArgumentException("Argument is not the same type as this instance.", "obj");
}//if
}//if
return CompareTo(other);
}
#endregion IComparable Members
#region IComparable<MyData> Members
public int CompareTo(MyData other) {
if(other == null) {
return 1; // Some value, that greater then zero.
}//if
var compare = StringComparer.Ordinal.Compare(Name, other.Name);
if(compare == 0) {
compare = Comparer<MyValue>.Default.Compare(Value, other.Value);
}//if
return compare;
}
#endregion IComparable<MyData> Members
public static bool operator <(MyData left, MyData right) {
return right != null && right.CompareTo(left) > 0;
}
public static bool operator >(MyData left, MyData right) {
return left != null && left.CompareTo(right) > 0;
}
public static bool operator <=(MyData left, MyData right) {
return !(left > right);
}
public static bool operator >=(MyData left, MyData right) {
return !(left < right);
}
}
Особенности:
- Реализация IComparable.CompareTo(object), как и операторы < и >, учитывают, что аргумент (или оба) могут содержать пустую ссылку (null). Не-null-объект всегда больше null и два null-объекта считаются равными.
Ну вот и всё, в завершении осталось опубликовать снипетты, которые позволяют быстро добавить описанную реализацию к вашему типу. Сниппет для значимого типа:
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Implements IComparable and IComparable<> interfaces for a value type</Title>
<Shortcut>comparablestruct</Shortcut>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal Editable="false">
<ID>TypeName</ID>
<Function>ClassName()</Function>
</Literal>
<Literal Editable="false">
<ID>ArgumentException</ID>
<Function>SimpleTypeName(global::System.ArgumentException)</Function>
</Literal>
<Literal Editable="false">
<ID>NotImplementedException</ID>
<Function>SimpleTypeName(global::System.NotImplementedException)</Function>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[#region IComparable Members
int IComparable.CompareTo(object obj) {
if(!(obj is $TypeName$)) {
throw new $ArgumentException$("Argument is not the same type as this instance.", "obj");
}//if
return CompareTo(($TypeName$)obj);
}
#endregion IComparable Members
#region IComparable<$TypeName$> Members
public int CompareTo($TypeName$ other) {
$end$throw new $NotImplementedException$();
}
#endregion IComparable<$TypeName$> Members
public static bool operator <($TypeName$ left, $TypeName$ right) {
return left.CompareTo(right) < 0;
}
public static bool operator >($TypeName$ left, $TypeName$ right) {
return left.CompareTo(right) > 0;
}
public static bool operator <=($TypeName$ left, $TypeName$ right) {
return !(left > right);
}
public static bool operator >=($TypeName$ left, $TypeName$ right) {
return !(left < right);
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
Сниппет для ссылочного типа:
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Implements IComparable and IComparable<> interface for a reference type</Title>
<Shortcut>comparableclass</Shortcut>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal Editable="false">
<ID>TypeName</ID>
<Function>ClassName()</Function>
</Literal>
<Literal Editable="false">
<ID>ArgumentException</ID>
<Function>SimpleTypeName(global::System.ArgumentException)</Function>
</Literal>
<Literal Editable="false">
<ID>NotImplementedException</ID>
<Function>SimpleTypeName(global::System.NotImplementedException)</Function>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[#region IComparable Members
int IComparable.CompareTo(object obj) {
var other = obj as $TypeName$;
if(other == null) {
if(obj == null) {
return 1; // Some value, that greater then zero.
} else {
throw new $ArgumentException$("Argument is not the same type as this instance.", "obj");
}//if
}//if
return CompareTo(other);
}
#endregion IComparable Members
#region IComparable<$TypeName$> Members
public int CompareTo($TypeName$ other) {
if(other == null) {
return 1; // Some value, that greater then zero.
}//if
$end$throw new $NotImplementedException$();
}
#endregion IComparable<$TypeName$> Members
public static bool operator <($TypeName$ left, $TypeName$ right) {
return right != null && right.CompareTo(left) > 0;
}
public static bool operator >($TypeName$ left, $TypeName$ right) {
return left != null && left.CompareTo(right) > 0;
}
public static bool operator <=($TypeName$ left, $TypeName$ right) {
return !(left > right);
}
public static bool operator >=($TypeName$ left, $TypeName$ right) {
return !(left < right);
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
Комментариев нет:
Отправить комментарий