Явная реализация интерфейса с помощью типа значений

Интерфейсы общего применения, принимающие параметры в форме ссылки на System.Object, встречаются очень часто. Такие интерфейсы обычно являются широко используемыми, не обобщенными интерфейсами. Например, вот как выглядит интерфейс IComaparable:

1
2
3
4
public interface IComparable
{
int CompareTo ( object obj );
}

В .NET 2.0 добавилась поддержка интерфейса IComparable, применение которого всегда должно рассматриваться наряду с IComparable для достижения большей безопасности типов.

В том, что метод CompareTo принимает такой общий тип, есть большой смысл, потому что очень неплохо иметь возможность передавать ему почти все, что угодно, чтобы увидеть, как переданный объект сравнивается с объектом, который реализует CompareTo. Когда речь идет исключительно о ссылочных типах, здесь не происходит никакой потери эффективности, поскольку преобразование ссылочных типов к типу System.Object и обратно обходится бесплатно во всех случаях. Но все несколько усложняется, когда речь заходит о типах значений.

Рассмотрим приведенный ниже код, чтобы увидеть неприглядные детали:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
public struct SomeValue : IComparable {
public SomeValue ( int n )  {
this.n = n;
}
public int CompareTo( object obj )  {
if ( obj is SomeValue )  {
SomeValue other = (SomeValue) obj;
return n - other.n;
} else {
throw new ArgumentException( "Неверный тип!" );
}
}
private int n;
}
public class EntryPoint {
static void Main()  {
SomeValue vail = new SomeValue ( 1 ) ;
SomeValue val2 = new SomeValue ( 2 ) ;
Console.WriteLine ( vail.CompareTo(val2)  );
}
}

В невинном выглядящем вызове WriteLine в Main производится сравнение vail и val2. Но давайте посмотрим внимательнее, сколько здесь потребуется операций упаковки. Поскольку CompareTo принимает объектную ссылку, val2 нужно упаковать в точке вызова метода. В случае явной реализации метода CompareTo пришлось бы выполнить приведение значения vail к интерфейсу IComparable, что опять означает затраты на упаковку. Но и внутри метода CompareTo кошмар, связанный с упаковкой, не заканчивается.

К счастью, при сравнении SomeValue с определенными типами можно воспользоваться оптимизацией. Для выполнения работы можно предусмотреть безопасную в отношении типов версию метода CompareTo, как показано в следующем коде:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
public struct SomeValue : IComparable {
public SomeValue ( int n )  {
this.n = n;
}
int IComparable.CompareTo( object obj )  {
if ( obj is SomeValue )  {
SomeValue other = (SomeValue) obj;
return n - other.n;
}
else {
throw new ArgumentException( "Неверный тип!" );
}
public int CompareTo( SomeValue other )  {
return n - other.n;
}
private int n;
}
public class EntryPoint {
static void Main()  {
SomeValue vail = new SomeValue ( 1 ) ;
SomeValue val2 = new SomeValue ( 2 ) ;
Console.WriteLine( vail.CompareTo(val2) );
}
}

В данном примере упаковка в вызове CompareTo полностью исключена. Это объясняется тем, что компилятор выбирает версию, наиболее подходящую для типа. В данном случае, поскольку метод IComparable. CompareTo реализован явно, существует только одна перегрузка CompareTo в общедоступном контракте SomeValue. Но даже если бы IComparable. CompareTo не был реализован явно, компилятор все равно выбрал бы версию, безопасную в отношении типов.

Типичный шаблон поведения включает сокрытие безтиповых версий от нечаянного применения, так что пользователь должен выполнять явную упаковку. Эта операция преобразует интерфейсную ссылку, чтобы добраться до безтиповой версии.

Главный итог состоит в том, что этой идиоме определенно нужно следовать при любой реализации интерфейса в типе значений, когда обнаруживается, что можно определить перегрузки с более высокой безопасностью типов, чем перечисленные в объявлении интерфейса. Избежание ненужной упаковки — всегда хорошая вещь, и пользователи будут благодарны за внимание к деталям и стремление к эффективности.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100