ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 3172

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Однако для использования фабрики вам нужен объект G e n e r i c F a c t o r y l < T , U>: // И н с т а н ц и р у е м с Т = S t u d e n t , U = s t r i n g

G e n e r i c F a c t o r y l < S t u d e n t ,

s t r i n g > f a c t l =

 

new

G e n e r i c F a c t o r y l < S t u d e n t , s t r i n g > ( ) ;

Вам также необходим метод C r e a t e ()

этой фабрики для получения объектов с ус­

тановленным именем:

 

// И с п о л ь з о в а н и е с т р о к о в о г о а р г у м е н т а

students

[1] =

f a c t l . C r e a t e ( " R i c h a r d C o r e y " ) ;

Вот что происходит внутри метода G e n e r i c F a c t o r y l < T , U> . C r e a t e ( ) :

public T

C r e a t e (U u)

 

{

T t = new T(); // Как и ранее, параметры конструктору / / н е п е р е д а ю т с я

t . S e t P a r a m e t e r ( u ) ; / / И с п о л ь з у е т с я м е т о д , п р е д о с т а в л е н н ы й / / при р е а л и з а ц и и I S e t t a b l e

return t;

}

Поскольку C r e a t e () может создавать только объекты без параметров, вы исполь­ зуете метод S e t P a r a m e t e r () для передачи параметра и объекту t. После этого можно вернуть объект S t u d e n t с установленным членом и м е н и — такой же, как если бы для его создания был вызван конструктор с одним параметром. Вы знаете, что тип Т имеет

метод S e t P a r a m e t e r () из-за ограничений, накладываемых

на класс S t u d e n t :

: I S e t t a b l e < s t r i n g > . Этот интерфейс гарантирует, что класс

S t u d e n t имеет метод

SetParameter ( ) .

 

Обсуждение решения

Насколько хорошее решение получено? Да, оно не является лучшим из тех, кото­ рые вы видели в своей практике. По сути, это обходной путь, но он приводит нас туда, куда нужно!

Но что делать, если конструктор объекта требует двух параметров? трех или че­ тырех? Увы, I S e t t a b l e < U > годится только для конструктора с одним параметром. В случае двух параметров вы должны добавить интерфейс I S e t t a b l e 2 < U , V>. Для трех — интерфейс I S e t t a b l e 3 < U , V, W> и т.д. Кроме того, для каждого из этих типов потребуется своя фабрика. Хорошая новость только в том, что крайне редко встречаются конструкторы более чем с пятью-шестью параметрами. Это и определяет, сколько ин­ терфейсов I S e t t a b l e < U , V , . . . > и фабрик вам понадобится.

Конечно, при желании класс может реализовывать как интерфейс I S e t t a b l e < U > , так и I S e t t a b l e 2 < U , V > . В этом случае вам потребуется реализовать как метод S e t P a r a m e t e r (U и ) , так и метод S e t P a r a m e t e r (U u,V v ) . ( Э т о — пере­ грузка, поскольку два метода S e t P a r a m e t e r () имеют разные сигнатуры.)

Глава 15. Обобщенное программирование

371


Часть VI

Великолепные десятки

Какая книга из серии Для чайников была бы полна без этой части? С# отлично умеет искать ошибки в ваших программах — вы, навер­ ное, и сами это заметили. Однако сообщения об ошибках часто на­ поминают военные шифровки — это тоже наверняка бросилось вам в глаза. В главе 16, "Десять наиболее распространенных ошибок компиляции", будут рассмотрены десять наиболее часто встречаю­ щихся ошибок в программах С#, а также будег рассказано, что они означают и как с ними бороться.

Многие читатели интересуются местом С# в семье объектноориентированных языков программирования и его связью с наиболее распространенным объектно-ориентированным языком — С++. В гла­ ве 17, "Десять основных отличий С# и С++", вкратце описаны отличия этих двух языков, включая различия между обобщенными классами С# и шаблонами С++.

Глава 16

Десять наиболее распространенных ошибок компиляции

>The name 'memberName' does not exist in the class or namespace 'className'

>Cannot implicitly convert type 'x' into 'y'

>'className.memberName' is inaccessible due to its protection level

> Use of unassigned local variable 'n'

} Unable to copy the file 'programName.exe' to 'programName.exe.' The process cannot ...

>'subclassName.methodName' hides inherited member 'baseclassName.methodName'. Use the new keyword if hiding was intended.

>'subclassName' : cannot inherit from sealed class 'baseclassName'

>'className' does not implement interface member 'methodName'

>'methodName' : not all code paths return a value

>j expected

# очень строго подходит к программам и буквально с лупой выискивает в них мельчайшие ошибки. В этой главе будут рассмотрены 10 наиболее распростра­ ненных сообщений об ошибках. Но перед тем как приступить к этому, следует сделать несколько замечаний. С# достаточно многословен, и при работе над книгой мне попада­ лись ошибки, сообщения о которых не помещались на одной странице, так что я обрезал некоторые сообщения до одной-двух первых строк. Кроме того, в сообщениях об ошибке С# часто вставляет имена переменных, методов или классов, с которыми эти ошибки связаны. Вместо конкретных имен здесь я использую такие имена, как v a r i a b l e N a m e , memberName или c l a s s N a m e . Наконец, С# не просто выводит имя класса— он пред­ почитает выводить его полностью, с указанием пространства имен, что тоже никак не

приводит к сокращению сообщения.

Это сообщение об ошибке может свидетельствовать о том, что вы забыли объявить переменную, как в следующем примере:


f o r ( i n d e x = 0; i n d e x < 1 0 ; i n d e x + + )

{

// . . . К а к и е - т о д е й с т в и я . . .

}

Переменная i n d e x нигде не определена (см. главу 3, "Объявление переменныхзначений", о том, как правильно объявлять переменные). Приведенный исходный теш должен быть переписан следующим образом:

f o r ( i n t i n d e x = 0 ; i n d e x < 1 0 ; i n d e x + + )

{

II . . . К а к и е - т о д е й с т в и я . . .

}

To же применимо и к членам класса (см. главу 6, "Объединение данных — классы и массивы").

Не менее вероятно неверное написание имени переменной. Приведенный далее фрагмент исходного текста — хорошая иллюстрация такой ошибки,

c l a s s S t u d e n t

{

p u b l i c

s t r i n g s S t u d e n t N a m e ;

p u b l i c

i n t n I D ;

}

c l a s s M y C l a s s

{

s t a t i c p u b l i c v o i d M y F u n c t i o n ( S t u d e n t s )

{

C o n s o l e . W r i t e L i n e ( " И м я C o n s o l e . W r i t e L i n e ( " I d

=" + s . s S t u d e n t N a m e ) ;

=" + s . n l d ) ;

}

}

Здесь проблема заключается в том, что M y F u n c t i o n O обращается к члену nld, в то время как настоящее имя члена — n I D . Хотя это очень похожие имена, С# не считает их одинаковыми. Программист написал n l d , но никакого n l d не существу­ ет, и С# честно пытается об этом сообщить. (В данном случае сообщение об ошибке

немного

отличается:

' c l a s s . memberName ' d o e s

n o t

c o n t a i n a defini ­

t i o n f o r

1 v a r i a b l e N a m e '. Более подробно об

этом

вопросе рассказывается

в главе 3, "Объявление переменных-значений".)

 

 

 

 

Менее распространена, но все же попадает в десятку самых распространенных,

ошибка, связанная с объявлением переменной в другой области видимости,

c l a s s MyClas s

 

 

 

 

 

{

 

 

 

 

 

 

 

 

s t a t i c

p u b l i c

v o i d A v e r a g e I n p u t ( )

 

 

 

 

{

 

 

 

 

 

 

 

 

 

i n t

nSum =

0;

 

 

 

 

 

i n t n C o u n t = 0;

 

 

 

 

 

w h i l e ( t r u e )

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

/ /

Считываем ч и с л о

 

 

 

 

 

s t r i n g s = C o n s o l e . R e a d L i n e ( ) ;

 

 

 

 

376

i n t n = I n t 3 2 . P a r s e ( s ) ;

 

 

 

 

/ /

Выход,

Часть

VI.

Великолепные десятки

 

е с л и в в е д е н о о т р и ц а т е л ь н о е

ч и с л о

 


i f

(n <

0)

 

 

 

{

 

 

 

 

 

 

b r e a k ;

 

 

 

 

}

 

 

 

 

 

/ /

Н а к о п л е н и е вводимых

ч и с е л

 

 

nSum +=

n;

 

 

 

n C o u n t + + ;

 

 

 

}

 

 

 

 

 

// Вывод

р е з у л ь т а т а

 

 

 

C o n s o l e . W r i t e L i n e ( " С у м м а р а в н а " + n S u m ) ;

C o n s o l e . W r i t e L i n e ( " С р е д н е е

р а в н о

"

+ nSum / < n C o u n t ) ;

// З д е с ь

г е н е р и р у е т с я сообщение

об

ошибке

C o n s o l e . W r i t e L i n e ( " З а в е р ш а ю щ е е з н а ч е н и е — " + s ) ;

Последняя строка этой функции некорректна. Дело в том, что переменная s ограни­ чена областью видимости, в которой определена, и вне цикла w h i l e она не видна (см. главу 5, "Управление потоком выполнения").

Эта ошибка обычно указывает на попытку использования двух переменных различно­ го типа в одном выражении, например:

int nAge = 1 0 ;

 

 

 

// Генерация сообщения

об ошибке

int n F a c t o r e d A g e =

2 . 0

*

nAge;

Проблема заключается

в том,

что 2 . 0 — переменная типа d o u b l e . Целое значение

нАде умножается на число с плавающей точкой 2.0, что в результате дает значение типа double. С# не в состоянии автоматически сохранить значение типа d o u b l e в перемен­ яй nFactoredAge типа i n t , потому что при этом может оказаться потерянной ин­ формация — скорее всего, дробная часть числа с плавающей точкой.

Некоторые преобразования не настолько очевидны, как в следующем примере:

class MyClass

{

s t a t i c p u b l i c f l o a t F l o a t T i m e s 2 ( f l o a t f )

{

// Г е н е р и р у е т с я сообщение об ошибке f l o a t f R e s u l t = 2 . 0 * f;

r e t u r n f R e s u l t ;

Вы можете решить, что здесь все в порядке, так как везде используется тип f l o a t . Яо дело в том, что 2.0 имеет тип не f l o a t , a d o u b l e , d o u b l e , умноженный на f l o a t , нет double. С# не может сохранить значение типа d o u b l e в переменной типа f l o a t в-за возможной потери информации, в данном случае — количества знаков результата, но приводит к снижению точности (см. главу 3, "Объявление переменных-значений").

Неявное преобразование легко запутывает неискушенного читателя. В приведенном да- фрагменте исходного текста функция F l o a t T i m e s 2 () работает вполне корректно:

та

16. Десять

наиболее распространенных ошибок компиляции

377