Перебор ячеек Excel. Помогите преобразовать код VBA в C#.

Автор Тема: Перебор ячеек Excel. Помогите преобразовать код VBA в C#.  (Прочитано 14969 раз)

0 Пользователей и 3 Гостей просматривают эту тему.

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн R.I.ChernovАвтор темы

  • ADN Club
  • *****
  • Сообщений: 568
  • Карма: 18
Доброго времени суток, уважаемые форумчане. Как и упомянуто в теме, прошу вашей помощи в преобразовании кода VBA в C#. Прошу прощения, что это немного оффтопик и не касается Inventor-а, очень надеюсь на ваше понимание. Исходный код в VBA:

Код - Visual Basic [Выбрать]
  1. Dim row_num As Integer
  2. Dim rng As Variant
  3.  
  4. For Each rng In xlsheet.range("A1:A90")
  5. If rng .Value = 75 Then
  6.     If xlsheet.range("B" & rng .Row).Value = 5 Then
  7.     row_num = rng .Row
  8.     End If
  9. End If
  10. Next

И вроде бы код должен быть простым и полностью аналогичным. но не получается записать условия If:
Код - C# [Выбрать]
  1. If (rng .Value == 75)
  2. ....
  3. If ( xlsheet.range("B" + rng .Row).Value == 5)

При запуске программа ругается в обоих местах :  Не удается применить оператор "==" к операндам типа "string" и "int".
Буду благодарен за любой совет!
« Последнее редактирование: 23-03-2016, 17:08:27 от R.I.Chernov »
В программировании я новичок...но ненадолго! ;)

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13886
  • Карма: 1788
  • Рыцарь ObjectARX
  • Skype: rivilis
В C# оператор сравнения не = , а ==
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн R.I.ChernovАвтор темы

  • ADN Club
  • *****
  • Сообщений: 568
  • Карма: 18
C# оператор сравнения не = , а ==
Спасибо, Александр, как раз зашел это исправить. Классика жанра: "Учи мат часть!" :)
В программировании я новичок...но ненадолго! ;)

Оффлайн mikazakov

  • ADN
  • *
  • Сообщений: 752
  • Карма: 195
  • Skype: mikazakov@mail.ru
Не удается применить оператор "==" к операндам типа "string" и "int".
Батенька, вы же юзаете C#, главная особенность C# это строгая типизация, т.е. никакого разврата с автоматическим приведением типов. Это позволяет избежать некоторых ошибок и повысить скорость работы программы.
На том клочке кода который вы приводите:
Код - C# [Выбрать]
  1. If (rng .Value == 75)

Я подразумеваю, что переменная rng объявлена как Object (т.к. Variant в C# не существует). И потом вы пытаетесь выдавить с ней свойства Value . А на основании чего компилятор должен понять, что в типе Object такое свойство есть? Он и не понимает этого, то что VBA отскладывает процесс проверки свойств на этап выполнения, не значит что C# так будет делать. Поэтому самое рациональное это приведение типов, например:
Код - C# [Выбрать]
  1. If ((int)rng .Value == 75)
После этого компилятор C# должен "проглотить" эту строку, т.к. ответственность за значение лежит теперь на вас.

Есть еще один подход в C# для случаев взаимодействиями объектными моделями на базе IDispatch (VBA), это объявить переменную с ключевым словом dynamic вместо Object:
Код - C# [Выбрать]
  1. dynamic rng;
тогда компилятор будет молчать и проверять тип переменной во время выполнения программы, но это нужно юзать как крайний случай, потому как это дорожка скользкая.

P.S. так как причесанного примера тут не было, поэтому все здесь на уровне теории, но думаю что вы додумаете как атм у вас правильно, что и где приводить.

Оффлайн R.I.ChernovАвтор темы

  • ADN Club
  • *****
  • Сообщений: 568
  • Карма: 18
Спасибо, Михаил. Я еще не до конца разобрался в тех мыслях, которые вы пытались до меня донести в своем сообщении, так что докучаю вас вопросами "по-порядку возникновения" ;)
Я подразумеваю, что переменная rng объявлена как Object (т.к. Variant в C# не существует). И потом вы пытаетесь выдавить с ней свойства Value .
Дело в том, что я использую тип Excel.Range, оболочка процедуры выглядит так:
Код - C# [Выбрать]
  1. foreach (Excel.Range cellNum in xlWorksheet.Range["A1:A90"])
  2. {
  3. }
где xlWorksheet ссылка на лист Excel.
Я покопался на форумах где обсуждается эксель API и, как я  понял, для такого перебора нужен именно тип range. В объектном браузере excel у "Range" указано два свойства Value и Value2. Полагаю что объектная модель неизменна при использовании C# и Excel API?  Вот с помощью них и пытаюсь получить значение ячеек для сравнения. Пока тщетно. Максимум, чего я пока добился, могу вывести номер строк с помощью Debug.WriteLine(cellNum.Row), что уже хорошо, ибо перебор работает в принципе. Причем если использовать Debug.Print(cellNum.Row.ToString), то привести к типу string не выходит, программа говорит что это группа методов и не преобразуется в string формат... В общем сижу и пытаюсь во всех направлениях расширять свои знания C#, например понять, почему  Debug.WriteLine работает, мне казалось что ее от личие от Debug.Print лишь в том, что к формату стринг она приводит "за тебя"?
В программировании я новичок...но ненадолго! ;)

Оффлайн mikazakov

  • ADN
  • *
  • Сообщений: 752
  • Карма: 195
  • Skype: mikazakov@mail.ru
В объектном браузере excel у "Range" указано два свойства Value и Value2.
Если конкретизировать, то здесь похоже, что свойство Value является типом Variant, аналога которого в C# нет. Variant он похож на Object, и тоже позволяет создавать переменные "произвольного" типа. Как я писал выше что Variant в C# упаковывается в Object. И компилятор не может понять какой там тип упакован внутри. Поэтому нужно делать приведение типа у Range.Value . например к int.
Видимо Debug.WriteLine имеет алгоритм который пытается сам анализировать и если это успешно то выводит строку. Бейсик часто так делает.

Оффлайн R.I.ChernovАвтор темы

  • ADN Club
  • *****
  • Сообщений: 568
  • Карма: 18
Поэтому нужно делать приведение типа у Range.Value . например к int.
Спасибо, Михаил, я этим как раз и занимаюсь. Хотя в многочисленных примерах с форумов excel как правило встречается value2, так что изучаю оба эти свойства.
В программировании я новичок...но ненадолго! ;)

Оффлайн mikazakov

  • ADN
  • *
  • Сообщений: 752
  • Карма: 195
  • Skype: mikazakov@mail.ru
Хотя в многочисленных примерах с форумов excel как правило встречается value2
MSDN:

Свойство Value2, который можно использовать для объекта Range, практически идентичен свойство Value, за исключением того, что свойство Value2 не использует типы данных даты и валюты.


Оффлайн R.I.ChernovАвтор темы

  • ADN Club
  • *****
  • Сообщений: 568
  • Карма: 18
Да, спасибо, я читал справку. Быть может его предпочитают из за отсутствия аргумента. Я нашёл сейчас одну хорошую статью, в которой описывается, чем отличается программирование в C# в Excel от VBA. Надеюсь найду там все необходимое.
В программировании я новичок...но ненадолго! ;)

Оффлайн mikazakov

  • ADN
  • *
  • Сообщений: 752
  • Карма: 195
  • Skype: mikazakov@mail.ru
Быть может его предпочитают из за отсутствия аргумента.
Думаю, что дело в том, что старый валютный тип упразднен, вместо него сейчас Decimal, но конвертировать старый тип в новый немного заморочено. Видимо с датой тоже самое.
Но в вашем случае, батенька, нужно просто приводить к строковому типу или числовому.

Отмечено как Решение R.I.Chernov 25-03-2016, 16:22:37

Оффлайн R.I.ChernovАвтор темы

  • ADN Club
  • *****
  • Сообщений: 568
  • Карма: 18
Как говорится: "ларчик просто открывался!". Итоговый код (вдруг кому пригодится) получился таким :
Код - C# [Выбрать]
  1. foreach (Excel.Range cellNum in xlWorksheet.Range["A2:A90"])
  2. {
  3.     if (cellNum.Value ==  75)
  4.     {
  5.         if (xlWorksheet.Range["B" + cellNum.Row].Value2 == 3)
  6.         {
  7.         }
  8.     }            
  9. }
где xlWorksheet - ссылка на лист (стоит заметить, что работает и Value и Value2)

Ошибка была в том, что диапазон значений для перебора, помимо числовых значений содержал одно строковое (шапка столбца, название переменной). И, как бы я не пытался привести к какому либо из типов, для сравнения в условии "If", всегда была ошибка (причина, полагаю, понятна). Все спасибо большое за помощь.
П.С. изучая теорию касательно работы с экслеь, я обнаружил, что во многих источниках вместо свойства Workseet.Range, используют метод Workseet.get_Range(). Причем get_Range не выпадает в "подсказке" C# и его можно набить только руками. А в статье для работы с Excel 2003 написано, что для C# необходимо использовать именно get_Range. Вобщем у меня путаница в голове, был бы признателен, если кто-нибудь смог бы мне разъяснить разницу, и есть ли она. Складывается впесатление. что get_Range - пережиток прошлых версий.
В программировании я новичок...но ненадолго! ;)

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13886
  • Карма: 1788
  • Рыцарь ObjectARX
  • Skype: rivilis
Как раз рекомендуют использовать свойство Range, а не метод get_Range() : https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.worksheet.get_range
Цитировать
Gets a Microsoft.Office.Interop.Excel.Range object that represents a cell or a range of cells. Use the Range property instead of this method.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн R.I.ChernovАвтор темы

  • ADN Club
  • *****
  • Сообщений: 568
  • Карма: 18
Спасибо, Александр!
Use the Range property instead of this method.
Это справка, я собственно ею и руководствовался, решив не использовать get_Range.
Но я говорил про примеры из форумов:
- просто присутствие get_Range в коде, как здесь: http://www.cyberforum.ru/csharp-beginners/thread644870.html
- так и прямые словесные рекомендации по его использованию как, например, вот в этой статье (правда она очень старая): http://www.ishodniki.ru/art/artshow.php?id=474

У меня пытливый ум. :) Мне не нравится, когда я чего-то не понимаю. Задача решена, а душевного покоя нет! :)
В программировании я новичок...но ненадолго! ;)

Оффлайн mikazakov

  • ADN
  • *
  • Сообщений: 752
  • Карма: 195
  • Skype: mikazakov@mail.ru
Мне не нравится, когда я чего-то не понимаю.
get_Range и set_Range это тоже самое, что и Range[индекс]. Просто раньше индексаторы в C# не поддерживались, их ввели позже для удобства использования коллекций из COM-объектов.

Ну а по поводу разных типов в одном столбце... в перспективе это может быть очень гадко, потому как поставил незаметный пробел рядом с числом в ячейке и там уже не int, а string и все и приехали... поэтому лучше дополнительную проверку делать:

Код - C# [Выбрать]
  1.   object obj = "34";
  2. if (obj is string) Console.WriteLine("String");
  3. if (obj is int) Console.WriteLine("int");

Оффлайн R.I.ChernovАвтор темы

  • ADN Club
  • *****
  • Сообщений: 568
  • Карма: 18
поэтому лучше дополнительную проверку делать
Спасибо!
У меня появилась еще одна проблема с поим перебором. Повторно укажу код, чтобы удобнее было читать:
Код - C# [Выбрать]
  1.     foreach (Excel.Range cellNum in xlWorksheet.Range["A2:A90"])
  2.     {
  3.         if (cellNum.Value ==  75)
  4.         {
  5.             if (xlWorksheet.Range["B" + cellNum.Row].Value2 == 5)
  6.             {
  7.              cellNum_main = cellNum;
  8.              abc = (int) cellNum.Row;
  9.             }
  10.         }        
  11.     }
 
Цель перебора - найти нужную строку по двум изменяемым параметрам (лишь для простоты они записаны как 75 и 5). Lальше считать значения переменных из этой строки. Проблема моя в том, что значение cellNum.Row существует лишь внутри процедуры foreach. Я пытался создать переменные типа range и int и приравнять их  к cellNum и (int) cellNum.Row соответственно. Но когда я дальше (за пределами foreach) пытаюсь использовать эти переменный, программа пишет, что им не присвоено значение. :( Причем пишет еще до запуска отладки. Если знаете, посоветуйте пожалуйста, как это решить.
В программировании я новичок...но ненадолго! ;)