Обработка ошибок. Оператор On Error 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Обработка ошибок. Оператор On Error



Многие функции и операторы, которыми вы пользуетесь в Visual Basic, вовсе не обязаны при любых обстоятельствах успешно завершать свою работу. Например, вы запустили процедуру для чтения файла с дискеты:

Private Sub Command1_Click()

Open "a:\Файлик.txt" For Input As #1

Input #1, a

Close #1

Debug.Print a

End Sub

но забыли вставить дискету в дисковод. Оператор Open не может выполнить свою работу, Visual Basic выдает сообщение об ошибке и работа приложения прерывается. Рядовой пользователь, работающий с вашим приложением, окажется в затруднительном положении. Он совсем не обязан разбираться в английском тексте сообщения, и если он даже догадается, в чем дело, и вставит дискету, все равно приложение надо будет запускать заново, так как оно прервано. Вы, как программист, должны учитывать, что с вашим приложением будут работать рядовые пользователи, причем даже не очень квалифицированные. Поэтому при программировании вы должны предугадать все возможные неправильные действия пользователя, чтобы при таких действиях приложение не прерывалось, а выдавало вразумительное сообщение на русском языке и советы по выходу из затруднительной ситуации.

Кое-что в этом направлении мы уже делали в 5.9, предохраняя калькулятор от арифметических действий над текстом и от деления на ноль.

Самым примитивным способом защиты вышеприведенной программы будет такой:

Private Sub Command1_Click()

On Error GoTo m1

 

Open "a:\Файлик.txt" For Input As #1

Input #1, a

Close #1

Debug.Print a

Exit Sub

 

m1: MsgBox ("Ошибка при вводе файла. Возможно, в дисководе нет дискеты.")

End Sub

Пояснения: Полужирным шрифтом я выделил новые элементы по сравнению с предыдущей программой. Оператор On Error GoTo m1 переводится так: "При возникновении ошибки иди к метке m1". Оператор On Error с момента своего выполнения заставляет компьютер быть начеку, не появится ли ошибка при выполнении программы. При ее появлении сообщение Visual Basic об ошибке уже не возникает, программа не прерывается, а управление передается на метку. Поэтому мы должны ставить оператор On Error раньше, чем операторы, могущие дать ошибку (в нашем случае это Open и Input). После метки вы должны поставить операторы, объясняющие пользователю, в чем дело, и помогающие как-то исправить ситуацию. В нашем случае никаких операторов, кроме MsgBox, не нужно. Забывчивый пользователь вставляет дискету в дисковод и снова жмет кнопку Command1. Все в порядке.

Обратите внимание на оператор Exit Sub. Если бы его не было, оператор MsgBox выполнялся бы всегда, даже при нормальной работе процедуры.

 

Написанный нами код слишком примитивен. Во-первых, мы не можем понять, какой оператор дал ошибку - Open или Input. Во-вторых, причин ошибки может быть несколько: отсутствие дискеты в дисководе, отсутствие файла на дискете и т.п. В первом случае мы вдобавок к оператору On ErrorGoTo m1 вставляем после Open, но перед Input, операторOn Error GoTo m2. В результате ошибка в строке Input будет обрабатываться операторами, начинающимися с метки m2., а ошибка в строке Open будет продолжать обрабатываться операторами, начинающимися с метки m1.

Во втором случае к нашим услугам объект Err, свойство которого Number принимает разные значения в зависимости от характера ошибки. Мы можем, например, писать такие строки:

If Err.Number = 71 Then

MsgBox ("В дисководе нет дискеты.")

ElseIf …………

Номера и имена констант ошибок вы найдете так: Help®Contents® MSDN Library (если она у вас установлена) ®Visual Studio documentation ®Visual Basic documentation®Reference®Trappable Errors.

 

Глава 17. Функции. Параметры процедур и функций

Процедуры и функции с параметрами - важное средство сделать вашу программу надежнее и понятнее.

Функции. Параметры функций

Мы с вами уже сталкивались со стандартными функциями. Например, выражение 10+Abs(-20) имеет значение 30, так как функция Abs(-20) обозначает абсолютную величину числа -20. Стандартная функция - это некая скрытая программа, которая принимает свои параметры, указанные в скобках, в качестве исходных данных, что-то делает с ними и в результате получает одну величину, которая и является значением функции. Множество других примеров функций вы найдете в 11.1, 16.1 и по всей книге.

Если вам недостаточно стандартных функций, вы можете создавать собственные функции пользователя:

 

Задача: Предположим, вам часто приходится вычислять периметры прямоугольников. Тогда вам было бы удобно иметь функцию вида perimetr(10,4), которая имела бы значение периметра прямоугольника со сторонами 10 и 4. Рассмотрим, как это делается, на примере программы вычисления суммарного периметра трех прямоугольников:

Private Function perimetr(dlina As Integer, shirina As Integer) As Long

perimetr = 2 * (dlina + shirina)

End Function

 

Private Sub Command1_Click()

Debug.Print perimetr(10, 4) + perimetr(20, 30) + perimetr(3, 8)

End Sub

Здесь щелчком по кнопке Command1 мы запускаем вычисление суммы периметров трех прямоугольников: 10*4, 20*30 и 3*8. А для того, чтобы узнать, как вычислять perimetr, компьютер заглядывает в определение функции, выделенное мной полужирным шрифтом.

Вы видите, что определение функции очень похоже на определение процедуры. Но функция в отличие от процедуры обладает некоторыми свойствами переменной величины и поэтому определение функции отличается от определения процедуры следующими двумя вещами:

· В заголовке функции после скобок с параметрами должен быть указан тип функции (у нас это Long).

· Внутри определения функции ей хотя бы раз должно быть присвоено какое-нибудь значение, как если бы это была не функция, а обычная переменная (у нас этим занимается оператор perimetr=2*(dlina+shirina)).

Обращение к функции также отличается от обращения к процедуре. Если обращение к процедуре - самостоятельный оператор (Portos, Aramis из 10.2, Рисуем_мой_значок 100, 200, vbBlue из 10.5), то обращение к функции - это обычно составная часть выражения (10+Abs(-20), Debug.Print perimetr(10,4) + perimetr(20, 30)).

 

Рассмотрим другой пример функции:

Задача: Напишите функцию Ответ, которая бы в ответ на вопрос человека "Какое сегодня число?" принимала значение правильной даты, а ответом на любой другой вопрос должно служить "Не знаю".

Вот программа:

Dim q As String

 

Private Function Ответ(Вопрос As String) As String

If Вопрос = "Какое сегодня число?" Then Ответ = Date Else Ответ = "Не знаю"

End Function

 

Private Sub Command1_Click()

q = InputBox("Задайте вопрос")

Debug.Print Ответ(q)

End Sub

Здесь щелчком по кнопке Command1 мы вызываем появление на экране окна InputBox с предложением задать вопрос. Введенный нами вопрос запоминается в переменной q. Следующая строка (Debug.Print Ответ(q)) вызывает к жизни нашу функцию. При этом компьютер, выполняя тело функции, вместо параметра Вопрос работает с переменной q, так как именно она была указана в обращении к функции. Получается очень удобно: когда мы пишем функцию, нам не нужно заботиться о том, какие имена переменных будут использованы при обращении к функции, мы просто даем параметру любое пришедшее в голову имя. И наоборот, когда мы пишем обращение к функции, нам не нужно заботиться о том, какие имена имеют параметры в заголовке функции.

 

Задание 128: Напишите функцию, вычисляющую любое число Фибоначчи. Например, fib(8) - это 8-е число Фибоначчи.

Указание: Если будете использовать массив, то дайте ему имя, отличающееся от имени функции, например, f.

Локальные переменные

Материал следующих разделов этой главы сложен для восприятия и на первый взгляд непонятно, зачем он нужен. Но для уверенного плавания по океану Visual Basic он необходим. Итак, наберите побольше терпения и кислорода. Идем вглубь.

 

Будем называть процедуры и функции подпрограммами, так как они являются составными частями программы.

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

Если же переменные объявлены внутри подпрограммы или являются параметрами подпрограммы, то они и использованы могут быть только внутри нее. Говорят, что они не видны из других подпрограмм. Их называют локальными переменными подпрограммы. О так называемых глобальных переменных поговорим позже (19.4).

Пример:

Dim a As Integer 'a - локальная переменная формы

 

Private Sub Проц1()

a = 1

End Sub

 

Private Sub Проц2()

Dim b As Integer 'b - локальная переменная процедуры

b = 2

End Sub

 

Private Sub Проц3(c As Integer) 'c - локальная переменная процедуры

Debug.Print c

End Sub

 

Private Sub Command1_Click()

Проц1

Debug.Print a

End Sub

Здесь a - локальная переменная формы, b и c - локальные переменные процедур. Вы видите, что a нормально используется в двух процедурах. Запустим проект. Щелкнем по кнопке Command1. Печатается 1. Все в порядке. Процедуры Проц2 и Проц3 здесь не работали. Они приведены только для иллюстрации.

Теперь попробуем использовать b вне процедуры Проц2. Для этого добавим к программе такую процедуру:

Private Sub Command2_Click()

Проц2

Debug.Print b 'Ошибочный оператор. Переменная b из процедуры Проц2 отсюда не видна и не будет напечатана

End Sub

Щелкнем по кнопке Command2. Вместо 2 печатается пустая строка. Также неудачей закончится попытка использования c.

 

Зачем такие ограничения и неудобства? Какой во всем этом смысл? Поговорим об этом.

Создание разных зон видимости для разных переменных является способом повышения надежности больших программ и понижения вероятности запутаться при их написании. Программы, создаваемые сегодня профессиональными программистами, очень велики - десятки и сотни тысяч строк. Таково, например, большинство игровых программ. Естественно, один человек не может достаточно быстро создать такую программу, поэтому пишется она обычно большой группой программистов. Для этого программа делится на части, и каждый программист пишет свою часть. И все равно, в каждой части присутствуют десятки и сотни подпрограмм с десятками и сотнями переменных, в которых человеку легко запутаться.

Исследуем взаимодействие подпрограмм в такой программе. Для этого рассмотрим бессмысленную "сложную" программу, не использующую локальных переменных подпрограмм. В ней автор озабочен вычислением переменной y и печатью переменной x:

Dim x As Integer 'x - локальная переменная формы

Dim y As Integer 'y - локальная переменная формы

 

Private Sub B()

y = 10 * 10

Debug.Print "Результат равен";

End Sub

 

Private Sub Command1_Click()

x = 5

B

Debug.Print x

End Sub

Очевидно, программа, как и хотел ее автор, напечатает:

Результат равен 5

 

Все хорошо. Но при большом объеме программы возникает опасность, что автор случайно использует внутри какой-нибудь подпрограммы для ее нужд имя переменной, используемой в другой подпрограмме для других нужд, и таким образом испортит ее значение. Пусть, например, в нашей процедуре В автор опрометчиво присвоил бы значение 10*10 не переменной с именем y, а переменной с именем x. Тогда эта строчка программы выглядела бы так:

 

x = 10 * 10

Очевидно, данная программа к неудовольствию ее автора напечатала бы

Результат равен 100

Для защиты от таких ошибок автор должен внимательно следить, чтобы разные подпрограммы не использовали переменных с одинаковыми именами. Но для больших программ этот контроль очень трудоемок и неудобен. Для того, чтобы избежать его, в современных языках программирования и разработан механизм локальных переменных. Если программист знает, что его число 10*10 нигде, кроме как в процедуре В, не нужно, он объявляет соответствующую переменную любым именем внутри процедуры В, ничуть не заботясь, что переменные с таким же именем встречаются в других местах программы, и все нормально работает:

Dim x As Integer 'x - локальная переменная формы

 

Private Sub B()

Dim x As Integer 'x - локальная переменная процедуры

x = 10 * 10

Debug.Print "Результат равен";

End Sub

 

Private Sub Command1_Click()

x = 5

B

Debug.Print x

End Sub

Данная программа к удовольствию ее автора напечатает

Результат равен 5

Произойдет это вот по какой причине: Переменные, объявленные внутри и снаружи подпрограммы или внутри разных подпрограмм, Visual Basic считает разными переменными, даже если они имеют одинаковые имена. Переменная х, объявленная снаружи подпрограммы, это совсем другая переменная, чем х, объявленная в подпрограмме, и помещаются эти переменные в разных местах памяти. Поэтому и не могут друг друга испортить. Вы можете вообразить, что это переменные с разными именами xлокформ и xлокпроц.

Переменная, объявленная внутри подпрограммы, невидима снаружи. Она локальна в этой подпрограмме. Она существует, пока работает подпрограмма, и исчезает при выходе из подпрограммы.

Переменная же, объявленная снаружи подпрограммы, видна из любой подпрограммы окна кода и каждая подпрограмма может ее испортить. Однако, когда подпрограмма натыкается на переменную x, объявленную внутри самой этой подпрограммы, то она, благодаря описанному механизму, работает только с ней и не трогает переменную x, объявленную снаружи.

 

Для тренировки рассмотрим еще один пример:

Dim x As Integer 'x - локальная переменная формы

Dim z As Integer 'z - локальная переменная формы

 

Private Sub B()

Dim x As Integer 'x - локальная переменная процедуры

Dim y As Integer 'y - локальная переменная процедуры

x = 20: y = 30: z = 40

End Sub

 

Private Sub Command1_Click()

x = 1: z = 2 'x и z - локальные переменные формы

B

Debug.Print x, z 'x и z - локальные переменные формы

End Sub

Программа напечатает

1 40

Пояснение: Оператор Debug.Print x, z находится снаружи процедуры В, и поэтому локальная переменная процедуры х=20, объявленная внутри В, из него не видна. Зато прекрасно видна локальная переменная формы х=1, которую он и печатает. Переменная же z не объявлена внутри В, поэтому она является локальной переменной формы, и оператор z=40 с полным правом меняет ее значение с 2 на 40.

Для полной ясности приведу порядок работы компьютера с этой программой:

 

В оперативной памяти Visual Basic отводит ячейки под Х локформ и Z локформ.

Процедура Command1_Click начинает выполняться с присвоения значений Х локформ = 1 и Z локформ = 2. Почему локформ, а не локпроц? Потому что переменные с именами X и Z в процедуре Command1_Click не объявлены, а значит волей-неволей процедуре приходится пользоваться локальными переменными формы.

Вызывается процедура В. При этом в оперативной памяти отводится место под Х локпроц и У локпроц.

Присваиваются значения Х локпроц = 20, У локпроц = 30 и Z локформ = 40.

Программа выходит из процедуры В. При этом исчезают переменные Х локпроц = 20 и У локпроц = 30.

Компьютер печатает Х локформ = 1 и Z локформ = 40.

 

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

Статические переменные

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

Private Sub Command1_Click()

Dim Число As Integer

Dim Сумма As Integer

Число = Text1.Text

Сумма = Сумма + Число

End Sub

Чтобы она не обнулялась, объявим сумму статической переменной:

Private Sub Command1_Click()

Dim Число As Integer

Static Сумма As Integer

Число = Text1.Text

Сумма = Сумма + Число

End Sub

Теперь все в порядке.

Массивы как параметры

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

y = perimetr (10, 4)

можно было бы написать

a = 4: y = perimetr (10, a)

или

a = 3: y = perimetr (10, a+1).

 

Параметры процедур и функций можно объявлять, как имеющие самые разные типы, причем не только простые, но и сложные. Если вы не знаете, как правильно объявить тип параметра в заголовке функции или процедуры, можете объявить его как Variant. Рассмотрим для иллюстрации пример с массивами.

Задача: Имеется два массива, по два числа в каждом. Напечатать сумму элементов каждого массива. Использовать функцию sum, единственным параметром которой является суммируемый массив.

Программа:

Dim a(1 To 2) As Integer

Dim b(1 To 2) As Integer

 

Private Function sum(c As Variant) As Integer

sum = c(1) + c(2)

End Function

 

Private Sub Command1_Click()

a(1) = 10: a(2) = 20

b(1) = 40: b(2) = 50

Debug.Print sum(a), sum(b)

End Sub

Вычисляя функцию sum(a), Visual Basic работает с ячейками для элементов массива a, делая с ними все то, что операторы тела функции должны делать со значениями элементов массива c. Вычисляя же функцию sum(b), Visual Basic аналогично работает с ячейками для элементов массива b.

 

Задание 129. В школе два класса. В каждом - 5 учеников. Каждый ученик получил отметку на экзамене по физике. Определить, какой из двух классов учится ровнее (будем считать, что ровнее учится тот класс, в котором разница между самой высокой и самой низкой отметкой меньше).

Указание: Создать функции Минимум(c), Максимум(c) и Разница(c).



Поделиться:


Последнее изменение этой страницы: 2016-08-12; просмотров: 172; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.84.228.68 (0.095 с.)