Глава.13 .Интерфейсы
Эти два изменения обеспечили безопасность типов при компиляции и избавили
от упаковки:
public static void Main() {
SomeValueType v = new SomeValueType(0);
Object o = new Object();
Int32 n = v.CompareTo(v); // Без упаковки
n = v.CompareTo(o); // Ошибка компиляции
}
Однако если определить переменную интерфейсного типа, то мы потеряем без-
опасность типов при компиляции и опять вернемся к упаковке:
public static void Main() {
SomeValueType v = new SomeValueType(0);
IComparable c = v; // Упаковка!
Object o = new Object();
Int32 n = c.CompareTo(v); // Нежелательная упаковка
n = c.CompareTo(o); // Исключение InvalidCastException
}
Как уже отмечалось, при приведении экземплярного типа к интерфейсному
среда CLR должна упаковывать экземпляр значимого типа. Поэтому в приведенном
методе
Main
выполняются две упаковки.
К EIMI часто прибегают при реализации таких интерфейсов, как
IConvertible
,
ICollection
,
IList
и
IDictionary
. Это позволяет обеспечить в интерфейсных методах
безопасность типов при компиляции и избавиться от упаковки значимых типов.
Опасности явной реализации
интерфейсных методов
Очень важно понимать некоторые особенности EIMI, из-за которых следует избе-
гать явной реализации интерфейсных методов везде, где это возможно. К счастью,
в некоторых случаях вместо EIMI можно обойтись обобщенными интерфейсами.
Но все равно остаются ситуации, когда без EIMI не обойтись (например, при ре-
ализации двух интерфейсных методов с одинаковыми именами и сигнатурами).
С явной реализацией интерфейсных методов связаны некоторые серьезные про-
блемы (далее я расскажу о них подробнее):
отсутствие документации, объясняющей, как именно тип реализует EIMI-метод,
а также отсутствие IntelliSense-поддержки в Microsoft Visual Studio;
при приведении к интерфейсному типу экземпляры значимого типа упаковы-
ваются;
EIMI нельзя вызвать из производного типа.
349
Опасности.явной.реализации.интерфейсных.методов
В описаниях методов типа в справочной документации .NET Framework можно
найти сведения о явной реализации методов интерфейсов, но справка по конкрет-
ным типам отсутствует — доступна только общая информация об интерфейсных
методах. Например, о типе
Int32
говорится, что он реализует все методы интерфейса
IConvertible
. И это хорошо, потому что разработчик знает, что такие методы су-
ществуют; с другой стороны, эта информация может создать проблемы, потому что
вызвать метод интерфейса
IConvertible
для
Int32
напрямую нельзя. Например,
следующий метод не скомпилируется.
public static void Main() {
Int32 x = 5;
Single s = x.ToSingle(null); // Попытка вызвать метод
// интерфейса IConvertible
}
При компиляции этого метода компилятор C# вернет следующую ошибку
(ошибка CS0117:
int
не содержит определения для
ToSingle
):
error CS0117: 'int' does not contain a definition for 'ToSingle'
Это сообщение об ошибке лишь запутывает разработчика; в нем утверждается,
что в типе
Int32
метод
ToSingle
не определен, хотя на самом деле это неправда.
Чтобы вызвать метод
ToSingle
типа
Int32
, сначала следует привести его к типу
IConvertible
:
public static void Main() {
Int32 x = 5;
Single s = ((IConvertible) x).ToSingle(null);
}
Требование приведения типа далеко не очевидно, многие разработчики не могут
самостоятельно до этого додуматься. Но на этом проблемы не заканчиваются — при
приведении значимого типа
Int32
к интерфейсному типу
IConvertible
значимый
тип упаковывается, что приводит к лишним затратам памяти и снижению произ-
водительности. Это вторая серьезная проблема.
Третья и, наверное, самая серьезная проблема с EIMI состоит в том, что явная
реализация интерфейсного метода не может вызываться из производного класса.
Вот пример:
internal class Base : IComparable {
// Явная реализация интерфейсного метода (EIMI)
Int32 IComparable.CompareTo(Object o) {
Console.WriteLine("Base's CompareTo");
return 0;
}
}
internal sealed class Derived : Base, IComparable {
// Открытый метод, также являющийся реализацией интерфейса
public Int32 CompareTo(Object o) {
продолжение
350
Do'stlaringiz bilan baham: |