Как вызвать функцию в си шарп
Всем здрасте .
Я так и не смог придумать адекватный текст заголовка, так как мой вопрос не вмещается в 3-5 слов.
Суть такова:
У меня есть семь функций, и массив bool[], содержащий семь значений, которые определяют, стоит ли вызывать каждую из семи функций.
Вопрос: можно ли это сделать циклически ?
Абстрактно:
for (byte i=0;i<7;i++) if (arr[i]) Вызвать функцию, к которой привязан этот элемент массива
Я знаю только один способ вызвать функцию - напрямую, по её имени. Но, к моему сожалению этот способ не позволяет мне вызывать так несколько функций так, как бы мне этого хотелось.
Можно ли так сделать, и если да - то как ?
Понятно, что эти семь функций надо где то сохранить, но я даже не знаю, как написать запрос в гугле.
На запрос "c# ссылка на функцию" я нашёл, что это из области неуправляемого кода, и не кошерно мешать управляемый и неуправляемый код.
Подпись ? Не, не слышал .
Последний раз редактировалось OmegaBerkut; 19.11.2016 в 23:30 .
OmegaBerkut |
Посмотреть профиль |
Найти ещё сообщения от OmegaBerkut |
Регистрация: 12.01.2011
Сообщений: 19,500
delegate, Action, Func, .
Участник клуба
Регистрация: 21.10.2015
Сообщений: 1,361
зачем отвязывать функцию от массива? лист
Как мне посоветовал Alex11223 - я создал массив, и обращаюсь к нему:
Func[] functions=new Func[7]; functions[0]=new Func(MyFunc1); // и так далее по аналогии // . output=functions[i].Invoke(input); // в цикле
При этом, в моём случае все мои функции выполняют изменение input таким образом, что на каждой итерации цикла в output сохраняется изменённая строка input. Но это уже другая история.
Сейчас у меня появился ещё один вопрос, по теме ссылок, только теперь не на функции, а на значения .
В моём классе есть несколько значений. У каждого значения есть компонент интерфейса, отвечающий за содержимое отдельно взятого значения.
Дабы не городить 16 обработчиков событий на каждый компонент (ровно столько у меня компонентов, и их значений в классе) - я хочу организовать редактирование значения, которое я предварительно сохраняю в свойство Tag каждого компонента (при его создании). Вопрос такой: как в Tag сохранять ссылку на значение класса, для дальнейшего редактирования значения через эту ссылку ?
Абстрактно:
textBox1.Tag=example.assigntext; // это должно быть сохранение ссылки на значение // . textBox1.Tag="Hello, world !"; // при этом example.assigntext должно измениться
На C++ так будет работать, если при записи значения перед example.assigntext поставить амперсанд (&) . Так как это не передача параметра в функцию - ref не прокатит. Как это дело организовать на C# ?
Подпись ? Не, не слышал .
OmegaBerkut |
Посмотреть профиль |
Найти ещё сообщения от OmegaBerkut |
Участник клуба
Регистрация: 21.10.2015
Сообщений: 1,361
example.assigntext = "Hello, world !";
Спокойный псих
Участник клуба
Регистрация: 19.03.2013
Сообщений: 1,538
come-on
У меня таких assigntext'ов 16 штук, ровно столько же и компонентов интерфейса для их редактирования. Так конечно сделать можно, но будет некрасиво - 16 обработчиков событий.
Подпись ? Не, не слышал .
OmegaBerkut |
Посмотреть профиль |
Найти ещё сообщения от OmegaBerkut |
Регистрация: 28.01.2009
Сообщений: 20,999
sender это тот на ком вызвано событие
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса |
Посмотреть профиль |
Найти ещё сообщения от Пепел Феникса |
Спокойный псих
Участник клуба
Регистрация: 19.03.2013
Сообщений: 1,538
Пепел Феникса
Та же яичница, только в профиль: 16 кейсов. UPD: поправочка - для самих сендеров кейс неприменим. Так что - (sender as TextBox).Name.
Хотелось бы, что бы интерфейсная реализация содержала один обработчик события для редактирования одного типа данных. При этом - код обработчика может содержать всего одну строку. Я знаю, что такое можно сделать на С++, спрашиваю про аналог на C#.
Для примера - код обработчика события интерфейсной реализации:
((sender as CheckBox).Tag as Control).Enabled=(sender as CheckBox).Checked;
Таким образом у каждого CheckBox на форме существует свой контрол, который нужно включать/выключать.
Но это экземпляры классов - с ними, в данном случае, проще.
Как быть с переменными ?
Подпись ? Не, не слышал .
Последний раз редактировалось OmegaBerkut; 20.11.2016 в 21:10 .
OmegaBerkut |
Посмотреть профиль |
Найти ещё сообщения от OmegaBerkut |
Регистрация: 28.01.2009
Сообщений: 20,999
у вас есть лямбды, у вас есть делегаты, вы можете создавать структуры/классы.
полет фантазии неограничен.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса |
Посмотреть профиль |
Найти ещё сообщения от Пепел Феникса |
Спокойный псих
Участник клуба
Регистрация: 19.03.2013
Сообщений: 1,538
Сообщение от Пепел Феникса
у вас есть лямбды, у вас есть делегаты, вы можете создавать структуры/классы.
полет фантазии неограничен.
Я много чего не знаю . По C#, ровно как и по С++. И, есть те вещи, которые я знаю, как делаются в C++, но не знаю, как делаются в C#; ровно как и наоборот.
Так вот .
Что такое лямбды - я не знаю (не приходилось ещё сталкиваться) .
Что такое делегаты - я имею представление . Если говорить аналогиями - это зародыши генериков. Но опять же - сталкиваться непосредственно с делегатами приходилось только при обращении к компоненту из потока.
К чему я веду всю эту ересь .
Я задал вполне корректный и конкретный вопрос - как сделать это, привёл аналогию из родственного языка, написал псевдо-код того, что я хочу получить.
А в ответе получаю словосочетания без инструкций к их применению. Ну, или то, что другие люди называют "философией программирования" - в моём маленьком круге студентов-программистов под этим выражением подразумевается несуразная ересь. Или необоснованные, ни с чем не связанные данные.
Как вы лично, Пепел Феникса, предлагаете мне на такое реагировать ?
Говоря проще - внятного ответа на свой (второй) вопрос я так и не получил.
P. S. Я понимаю, что мне тут никто ничего не должен; но - если отвечаете, то отвечайте, пожалуйста, по существу.
Подпись ? Не, не слышал .
Последний раз редактировалось OmegaBerkut; 21.11.2016 в 00:40 .
OmegaBerkut |
Посмотреть профиль |
Найти ещё сообщения от OmegaBerkut |
Как вызвать функцию в си шарп
Локальные функции представляют функции, определенные внутри других методов. Локальная функция, как правило, содержит действия, которые применяются только в рамках ее метода.
Например, определим метод который сравнивают сумму чисел двух массивов:
void Compare(int[] numbers1, int[] numbers2) < int numbers1Sum = 0; int numbers2Sum = 0; foreach(int number in numbers1) numbers1Sum += number; foreach (int number in numbers2) numbers2Sum += number; if (numbers1Sum >numbers2Sum) Console.WriteLine("сумма чисел из массива numbers1 больше"); else if (numbers1Sum < numbers2Sum) Console.WriteLine("сумма чисел из массива numbers2 больше"); else Console.WriteLine("суммы чисел обоих массивов равны"); >int[] numbers1 = < 1, 2, 3 >; int[] numbers2 = < 3, 4, 5, 6, 7 >; Compare(numbers1, numbers2);
Здесь метод Compare принимает два массива и последовательно вычисляет сумму их элементов, чтобы узнать в каком массиве сумма чисел больше. Несмотря на то, что все работает, здесь есть один недостаток: здесь повторяется действия, которые вычисляют сумму массива:
int numbers1Sum = 0; foreach(int number in numbers1) numbers1Sum += number;
К тому а что, если мы захотим сравнивать сумму только положительных или четных чисел или как-то иначе изменить логику сравнения? В этом лучше вынести повторяющиеся действия в отдельный метод. Однако если эти действия нигде больше в прогамме не будут вызываться и будут использоваться только в одном методе, то целесообразно определить эти действия в виде локальной функции. Для этого изменим метод Compare следующим образом:
void Compare(int[] numbers1, int[] numbers2) < int numbers1Sum = Sum(numbers1); int numbers2Sum = Sum(numbers2); if (numbers1Sum >numbers2Sum) Console.WriteLine("сумма чисел из массива numbers1 больше"); else if (numbers1Sum < numbers2Sum) Console.WriteLine("сумма чисел из массива numbers2 больше"); else Console.WriteLine("суммы чисел обоих массивов равны"); int Sum(int[] numbers) < int result = 0; foreach (int number in numbers) result += number; return result; >> int[] numbers1 = < 1, 2, 3 >; int[] numbers2 = < 3, 4, 5, 6, 7 >; Compare(numbers1, numbers2);
Здесь подсчет суммы вынесен в локальную функцию Sum , которая принимает массив и возвращает его сумму. И далее в рамках метода Compare мы сможем ее использовать для вычисления суммы массива. При этом неважно, определена локальная функция до или после использования. Но вне ее метода локальная функция не может использоваться.
Статические локальные функции
Локлальные функции могут быть статическими. Такие функции определяются с помощью ключевого слова static . Их особенностью является то, что они не могут обращаться к переменным окружения, то есть метода, в котором статическая функция определена.
Сначала определим локальную функцию, которая имеет доступ окружению:
int Sum(int[] numbers) < int limit = 0; int result = 0; foreach (int number in numbers) < if (IsPassed(number)) result += number; >return result; bool IsPassed(int number) < return number >limit; > > int[] numbers1 = < -3, -2, -1, 0, 1, 2, 3 >; int[] numbers2 = < 3, -4, 5, -6, 7 >; Console.WriteLine(Sum(numbers1)); Console.WriteLine(Sum(numbers2));
Здесь функция Sum вычисляет сумму чисел массива, которые соответствуют условию в локальной функции IsPassed() . Эта локальная функция проверяет, больше ли переданное число чем значение переменной limit , определенной в методе Sum. То есть локальная функция IsPassed может обращаться к данным определенным в окружающей функции Sum.
Теперь сделаем функцию IsPassed статической:
int Sum(int[] numbers) < int result = 0; int limit = 0; foreach (int number in numbers) < if (IsPassed(number, limit)) result += number; >return result; static bool IsPassed(int number, int lim) < //return number >limit; // Ошибка: метод IsPassed не имеет доступа к переменной limit return number > lim; > >
Модификатор static указывается перед типом локальной функции. Теперь функция IsPassed не может обращаться к переменной limit, и в этом случае нам надо либо передать это значение в виде параметра, либо определить переменную limit непосредственно в локальной функции.
Записная книжка программиста-новичка, C#, SQL, PHP и все-все-все
Я ведь это уже делал, но хрен теперь найдешь тот кусок кода, гуглим снова… Где бы найти простое и понятное руководство для начинающего, а не тонкости для мега-гуру?
Главная→C#→Самоучитель C#→ Самоучитель по C# для начинающих. 02. Функции, классы, обьекты, коллекции
Рубрики
Свежие записи
- Вырезаем числовую часть из начала строки в transact-sql
- Пул соединений с базой данных в ADO.NET / OleDb - как избежать проблем с утечкой соединений в ASP.NET (перевод)
- ASP .Net MVC, JQuery и AJAX - отсылаем данные на сервер
- Разделитель тысяч и дробной части для decimal
- Создаем расширенный Control в WinForms наследуясь от существующего
- Вставка строк и изменение границ ячеек в Excel Interop из C#
- Как прочитать данные из удаленного DataRow в DataTable
- Проблемы с кодировкой при копировании русского текста из MS SQL Studio в Outlook/Word/Office
- Как проверить, существует ли таблица в MS SQL Server перед удалением/созданием
- Очень просто.
- Получаем выбранные строки DataGridView, в котором выбраны только ячейки
- Ошибка в коде привела к убыткам в 476 миллионов долларов и банкротству компании
- Отслеживаем изменения выбранного значения в колонке ComboBox DataGridView (DataGridViewComboBoxColumn)
- Excel 2010, Windows 7, два монитора и "ошибка при направлении команды приложению"
- Удаляем default-ограничение (constraint) в Transact Sql
Свежие комментарии
- Kirill к записи Самоучитель по C# для начинающих. 01. Основы языка, переменные, логика, циклы.
- как избавиться от чувства вины к записи Как добавить строку/текст в начало файла в C# и .Net
- DannyLef к записи Полезные расширения-плагины для WordPress
- как избавиться от чувства вины к записи Как добавить строку/текст в начало файла в C# и .Net
- gweg2ehgwEHERWQHQ к записи Простейшее диалоговое окно-вопрос (MessageBox) в WindowsForms
Архивы
Мета
Самоучитель по C# для начинающих. 02. Функции, классы, обьекты, коллекции
Опубликовано 15.08.2013 автором Ведомир
2.1 Функции.
Вернемся к старой задаче - выводе данных о людях. У нас есть разные люди с данными в виде отдельных фамилии, имени, отчества, которые надо вывести на экран - вида Пушкин Александр Сергеевич и Пушкин А.С. Чтобы задача была правдоподобней можно имитировать ввод данных пользователем или загрузку из внешнего источника, но все это будет пустой тратой времени - реальные приложения все равно работают с графическим и/или веб-интерфейсом. Просто держим в уме что в реальности людей не два, а две тысячи и заранее их имена не известны.
Получаем уродливый код
string name = "Александр"; string otchestvo = "Сергеевич"; string surname = "Пушкин"; string name2 = "Наталья"; string otchestvo2 = "Николаевна"; string surname2 = "Гончарова"; System.Console.WriteLine(surname + " " + name + " " + otchestvo); System.Console.WriteLine(surname2 + " " + name2 + " " + otchestvo2); System.Console.ReadLine();
Два раза повторяется один и тот же кусок кода - склеивание трех строк в одну.
Два раза повторяется другой кусок кода - склеивание фамилии, первых букв имени и отчества, точек.
Логичнее всего вынести повторяющиеся куски кода в отдельное место, дать им имя и во всех остальных местах программы вызывать их по этому имени. Иными словами сделать функции - куски кода с собственными именем, которые принимают на вход какие-то данные, что-то с ними делают и возвращают обратно какие-то данные (хотя возможен вариант когда они ничего не принимают и не возвращают, просто что-то делают).
public static string CreateFio(string surname, string name, string otchestvo)
Слова public static отложим на пару минут в сторону, string означает, что функция вернет назад строку, CreateFio(string surname, string name, string otchestvo) - название функции и описание того, что она принимает на вход три строки.
Если бы функция ничего не принимала и ничего не возвращала, ее описание выглядело бы так
public static void CreateFio() < // фио мы здесь явно создать не сможем, вечная морока с этим тестовыми примерами >
Код фцнкции обрамляется фигурными скобками, значение возвращается с помощью ключевого слова return
Весь код тестовой программы с функциями
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestConsoleApplication < class Program < public static string CreateFio(string surname, string name, string otchestvo) < string fio = surname + " " + name + " " + otchestvo; return fio; >public static string CreateFioInitials(string surname, string name, string otchestvo) < string fio = surname + " " + name.Substring(0, 1) + ". " + otchestvo.Substring(0, 1) + "."; //Временно оставим в стороне код "Substring(0, 1)" - он просто вырезает первый символ из строки. return fio; >static void Main(string[] args) < string name = "Александр"; string otchestvo = "Сергеевич"; string surname = "Пушкин"; string name2 = "Наталья"; string otchestvo2 = "Николаевна"; string surname2 = "Гончарова"; System.Console.WriteLine(CreateFio(surname, name, otchestvo)); System.Console.WriteLine(CreateFioInitials(surname, name, otchestvo)); System.Console.WriteLine(CreateFio(surname2, name2, otchestvo2)); System.Console.WriteLine(CreateFioInitials(surname2, name2, otchestvo2)); System.Console.ReadLine(); >> >
2.2 Классы и объекты
Код выглядит менее уродливо, но с переменными surname, surname2 явно что-то не так. Особенно если людей станет больше. Неплохо бы собрать все данные, относящиеся к одному человеку, в кучку. И сразу добавить к ним те функции, которые не нужны другие данные.
По сути дела нам надо перейти от типа данных "строка" к новому типу данных "человек", в который на данный момент будут входить три строки - фамилия имя и отчество. Само собой для этого давным-давно придуманы специальные инструменты.
Сферический человек в вакууме, имеющий только три строки фамилии, имени и отчества - это класс. Класс еще можно сравнить с чертежом какого-то объекта, например ящика, который будет иметь размеры из трех чисел - высоты, ширины и глубины.
А вот каждый конкретный человек со своими значениями ФИО будет экземпляром класса человек. Точно так же как каждый ящик со своими значениями высоты, ширины и глубины будет являться экземпляром класса ящик. Само собой у каждого класса может быть масса других параметров - например и у человека и у ящика может быть параметр масса, у человека может быть параметр пол - мужской или женский, а вот у ящика пол вряд ли будет (хотя кто знает, что нам готовит непредсказуемое будущее)
Еще раз приведу весь текст программы (файл Program.cs), чтоб вы обратили внимание где именно находится описание класса.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestConsoleApplication < class Program < static void Main(string[] args) < // создаем два экземпляра класса человек с разными фио Person person1 = new Person("Пушкин", "Александр", "Сергеевич"); Person person2 = new Person("Гончарова", "Наталья", "Николаевна"); // выводим разные фио используя встроенные в класс функции - методы System.Console.WriteLine(person1.GetFio()); System.Console.WriteLine(person1.GetFioInitials()); System.Console.WriteLine(person2.GetFio()); System.Console.WriteLine(person2.GetFioInitials()); //статическая функция одинакова для всех экземпляров класса и вызывается с имени самого класса System.Console.WriteLine(Person.GetClassDescription()); System.Console.ReadLine(); >> // класс человека public class Person < // три строковых переменные-свойства, доступные извне класса - public public string Name = ""; public string Surname = ""; public string Otchestvo = ""; // конструктор, специальная функция, которая вызывается при создани экземпляра класса с помощью слова new public Person(string surname, string name, string otchestvo) < Name = name; Surname = surname; Otchestvo = otchestvo; >// этим функциям не надо подавать на вход данные - они используют данные экземпляра класса public string GetFio() < string fio = Surname + " " + Name + " " + Otchestvo; return fio; >public string GetFioInitials() < string fio = Surname + " " + Name.Substring(0, 1) + ". " + Otchestvo.Substring(0, 1) + "."; return fio; >// статическая функция не используют данные экземпляра, она одинакова для всех экземпляров. По сути дела класс для нее - просто название группы функций public static string GetClassDescription() < return "Класс Person. Хранит данные о человеке."; >> >
В профессиональном программировании считается дурным тоном давать свободный и бесконтрольный доступ к хранимым в классе данным. Чтобы запретить доступ извне к свойству Surname для него надо прописать другой модификатор доступа, вместо public - private. Записывать и считывать данные надо будет через специальные функции, аналогичные тем функциям, которые на самом деле что-то вычисляют в духе GetFio. Чтобы не возиться с написанием кучи функций типа GetName и SetName в C# применяется специальный механизм свойств, на основании которых уже при компиляции программы автоматически генерятся эти однотипные функции. Такой способ облегчить жизнь программисту называется синтаксическим сахаром. В Visual Studio есть даже специальная функция для этого.
В этом же меню есть много других крайне полезных пунктов - например переименование переменной, класса, пространства имен или функции сразу во всех местах проекта.
class Program < static void Main(string[] args) < // создаем два экземпляра класса человек с разными фио Person person1 = new Person("Пушкин", "Александр", "Сергеевич"); Person person2 = new Person("Гончарова", "Наталья", "Николаевна"); System.Console.WriteLine(person1.Fio); System.Console.WriteLine(person1.FioInitials); System.Console.WriteLine(person2.Fio); System.Console.WriteLine(person2.FioInitials); System.Console.WriteLine(Person.ClassDescription); System.Console.WriteLine(Person.ClassDescription); >> // класс человека public class Person < // три строковых переменные-свойства, доступные извне класса - public private string _name = ""; public string Name < get < return _name; >set < _name = value; >> private string _surname = ""; public string Surname < get < return _surname; >set < _surname = value; >> private string _otchestvo = ""; public string Otchestvo < get < return _otchestvo; >set < _otchestvo = value; >> public string Fio < get < string fio = Surname + " " + Name + " " + Otchestvo; return fio; >> public string FioInitials < get < string fio = Surname + " " + Name.Substring(0, 1) + ". " + Otchestvo.Substring(0, 1) + "."; return fio; >> // конструктор, специальная функция, которая вызывается при создани экземпляра класса с помощью слова new public Person(string surname, string name, string otchestvo) < Name = name; Surname = surname; Otchestvo = otchestvo; >public static string ClassDescription < get < return "Класс Person. Хранит данные о человеке."; >> >
Обратите внимание, насколько проще стал код, в котором создаются и выводятся на экран люди.
Person person1 = new Person("Пушкин", "Александр", "Сергеевич"); Person person2 = new Person("Гончарова", "Наталья", "Николаевна"); System.Console.WriteLine(person1.Fio); System.Console.WriteLine(person1.FioInitials); System.Console.WriteLine(person2.Fio); System.Console.WriteLine(person2.FioInitials); System.Console.WriteLine(Person.ClassDescription); System.Console.WriteLine(Person.ClassDescription);
В тестовом примере может показаться, что такое упрощение не стоит всей мороки с созданием класса и на самом деле является усложнением, но такое впечатление создается только из-за неестественной простоты самого тестового примера, реальные программы намного сложнее.
2.3 Структура программ реального мира. Пространства имен.
Реальное программирование на 99% состоит из использования уже готовых классов и функций, написанных другими программистами или написания собственных. Так как в реальности мы имеем дело с сотнями и тысячами сложных классов, состоящих из сотен свойств и методов, то даже классы приходится разбивать на группы - пространства имен.
Для нашей тестовой программы мы используем пространство имен TestConsoleApplication, состоящее из двух классов - Program и Person.
Класс Program автоматически создается для каждого Net приложения, это стандартный класс. Как правило он состоит из одной функции/метода static void Main(string[] args), которая выполняется при запуске приложения.
Разработчики C# и .Net очень любят классы. Просто очень-очень. Так что в итоге здесь все является классами и даже функции без классов не существуют. Если вам не нужны ни классы ни экземпляры, и вы хотите просто сделать нескольких функций - вам все равно придется создать класс, в который добавить несколько статических (не требующих создания экземпляра) функций.
Классическим пример такой функции может послужить умножение
public static int Multiply(int a, int b)
Которое где-то в другом месте вызывается как
System.Console.WriteLine(SomeClassName.Multiply(2, 2));
Но на самом деле в .Net уже есть такой класс со сборником арифметических функций Math - в который входят функции для вычисления всевозможных синусов, тангенсов, квадратных корней, округления чисел.
Другой пример класса который по сути своей является библиотекой функций - класс Convert, содержащий функции для преобразования одних типов данных в другие
int testNum = Convert.ToInt32("34");
В .Net Framework входит множество уже готовых классов от программистов Microsoft, разделенных на множество пространств имен. Самые-самые базовые входят в пространство имен System.
Теперь можно полностью обьяснить использовавшийся в этой и предыдущей статьях код
System.Console.WriteLine("Привет мир!");
В данном случае мы вызываем функцию public static void WriteLine(string value) из класса Console входящего в пространство имен System. И уже где-то там внутри нее, скорее всего еще несколькими уровнями ниже выполняется рисование по пикселям окна с консолью и наших букв.
Чтобы не писать каждый раз пространство имен используется директива using
и на самом деле если мы уже прописали
using System;
то вывод текста в консоль можно было бы записать проще (помешали лень и копирование в буфер)
Console.WriteLine(person1.GetFio());
В C# классами и обьектами является все, в том числе строки. Каждая строка - это на самом деле экземпляр класса String. Теперь можно понять и следующий код:
string fio = surname + " " + name.Substring(0, 1) + ". " + otchestvo.Substring(0, 1) + ".";
Здес мы вызываем один из методов класса String - public string Substring(int startIndex, int length), который возвращает кусок строки и принимает два аргумента, индекс символа, с которого надо начать вырезание подстроки (нумерация начинается с 0) и длину вырезаемой подстроки.
Если же нам не нужны методы вообще, просто хочется собрать в кучку данные - можно использовать структуры. С другой стороны можно создать специальный класс без функций, с одними данными - для этого есть умное название обьект для передачи данных (Data Transfer Object, DTO).
2.4 Особенности хранения данных в памяти. Ссылочные и простые типы данных. Область видимости переменных.
Большинство программистов C# напрямую не сталкивается с подробностями размещения данных в памяти, подробные данные нужны только опытным программистам для оптимизации скорости работы программы - вот статья с описанием разных низкоуровневых хитростей, для которых нужны продвинутые знания.
Но основы надо знать всем - сейчас поймете почему. Для любых данных нужно место в памяти компьютера. В некоторых языках вроде C и C++ программистам надо вручную выделять его при создании переменных и освобождать после завршения их использования, сталкиваясь с ошибками вроде утечек памяти.
В C# дела обстоят иначе. Для самых простых типов данных память компьютера выделяется автоматически, написав int counter; мы автоматически получим место в памяти, в которое будет по умолчанию записан 0.
Но большая часть данных хранится в экземплярах классов, память для которых выделяется при использовании ключевого слова new. По большому счету это тоже происходит автоматически, а после завершения использования обьекта память высвобождается сборщиком мусора. Но такие переменные являются ссылочными типами данных и ведут себя иначе. В них хранятся не сами данные, а ссылка на область в памяти с этими данными. Если в одну переменную записать значение другой переменной, то они обе будут указывать на одно и то же значение. Вот код, наглядно демонстрирующий эту разницу
string surname1 = "Пушкин "; string surname2 = "Гончарова"; // в surname1 записывается значение surname2 но сами переменный продолжают существовать независимо surname1 = surname2; surname2 = "ААА"; Console.WriteLine("Значение surname1: " + surname1); // Гончарова Console.WriteLine("Значение surname2: " + surname2); // ААА Person person1 = new Person("Пушкин", "Александр", "Сергеевич"); Person person2 = new Person("Гончарова", "Наталья", "Николаевна"); // в person1 записывается ссылка на обьект person2 теперь обе переменные ссылаются на один и тот же обьект класса Person person1 = person2; person2.Surname = "AAA"; Console.WriteLine("Значение person1.Surname: " + person1.Surname); // ААА Console.WriteLine("Значение person2.Surname: " + person2.Surname); // ААА Console.ReadLine();
Отличие структур от классов без функций в том, что данные структуры копируются из одной в другую при записи одной переменной в другую, а при записи одной ссылочной переменной в другую копируется только ссылка на данные, что при большом объеме данных может работать намного быстрей.
С другой стороны, чтобы реально скопировать данные из одного объекта ссылочной типа в другой (и в итоге получить два разных объекта с одинаковыми данными, а не две ссылки на один и тот же) придется писать специальный код.
Когда именно какой-то объект становится ненужным и попадает под внимание сборщика мусора? На самом деле особенности работы сборщика мусора относятся к достаточно продвинутым и глубоким вопросам, так как это очень умная машина. Но как минимум одно связанное с этим понятие надо знать любому программисту - область видимости переменных. В большинсвте случаев мы потеряем контроль над переменной после того, как она выйдет из этой области видимости - после этого она больше не будет доступна программисту и рано или поздно ее память будет освобождена сборщиком мусора. В большинстве случаев область видимости переменной ограничивается блоком кода, ограниченным двумя фигурными скобками. Если внутри такого блока есть еще один блок - то на него распространяется область видимости родительского блока. Пример сферической области видимости в вакууме
< int testVariable = 1; >testVariable = 2; // такая программа просто не скомпиллируется, прямо в VisualStudio выдаст ошибку The name 'testVariable' does not exist in the current context
А вот так все будет в порядке.
int testVariable = 1;
Другой вариант первой ошибки
int testVariable = 1;
А вот так все будет в порядке, поскольку переменные с одним и тем же именем объявляется в разных областях видимости одного и того же уровня, не зависящих друг от друга
Само собой в реальном программировании пустые блоки фигурных скобок не применяются, но все вышесказанное верно для всех тех случаев, когда они применяются осмысленно - логических условий, циклов, функций.
Наглядный пример более сложной задачи, при решении о которой необходимо помнить об особенностях работы сборщика мусора - использование Word для вывода данных в отчеты . В этом случае мы используем ресурс, неподвластный сборщику мусора - программу Word, запускаемую и управляемую из нашего кода. Если она становится ненужна, то ее надо закрывать вручную из кода - иначе на компьютере начнут плодится невидимые Word'ы в неограниченных количествах.
2.5 Массивы, коллекции и цикл foreach
Наверняка вы уже заметили нечто странное во всех этих примерах - их невозможно использовать для работы с большими объемами данных. Как хранить информацию о тысячах людей или точек на экране? Два базовых инструмента - массивы и коллекции разных типов, наиболее простым и распространенным из которых являются списки. Как несложно догадаться и то и то является классами, и то и то представляет собой набор значений, к которым можно обращаться по номеру-индексу, начинающемуся с нуля.
int[] intArray = new int[5]; int[,] multiDimensionalIntArray = new int[2, 3]; Person[] personsArray = new Person[2]; personsArray[0] = new Person("Пушкин", "Александр", "Сергеевич"); personsArray[1] = new Person("Гончарова", "Наталья", "Николаевна"); Console.WriteLine(personsArray.Count()); // выведет 2 Console.WriteLine(personsArray[1].Fio); // выведет Гончарова Наталья Николаевна List personsList = new List(); personsList.Add(new Person("Пушкин", "Александр", "Сергеевич")); personsList.Add(new Person("Гончарова", "Наталья", "Николаевна")); System.Console.WriteLine(personsList.Count()); // выведет 2 System.Console.WriteLine(personsList[0].Fio); // выведет Пушкин Александр Сергеевич for (int counter = 0; counter
В чем же отличие массивов от списков? Подробности относятся к продвинутым знаниям, но в целом под массив место в памяти выделяется сразу и если надо изменить его размер, то место приходится выделять заново. Под список фиксированный объем не выделяется, он меняется динамически, но операции над элементами выполняются медленнее. Таким образом массив быстрее и лучше работает, если количество элементов не меняется, список - если меняется. Но в большинстве случаев эта разница в скорости не заметна на фоне других факторов. Продвинутые программисты хорошо понимают разницу между ArrayList, Dictionary, HashSet, LinkedList, Collection и другими типами коллекций.
Обходить все элементы в каком-то множестве приходится так часто, что в большинстве современных языков для этого сделали особый цикл, в C# это цикл foreach, "для каждого".
foreach (Person currPerson in personsList) < // currPerson - currentPerson - текущий человек, код внутри цикла будет вызываться раз за разом и в этой переменной последовательно окажутся все элементы последовательности, для каждого из которых можно будет что-то сделать Console.WriteLine(currPerson.FioInitials); >
Выйти из цикла можно с помощью ключевых слов break - просто прекращается выполнение цикла и continue - пропускает текущую итерацию и переходит к следующей
foreach (Person currPerson in personsList) < // пропускаем Пушкина и переходим к гончаровой if (currPerson.Surname == "Пушкин") < continue; >Console.WriteLine(currPerson.FioInitials); > foreach (Person currPerson in personsList) < // цикл не сделает ничего, так как на первом шаге его выполнение прервется if (currPerson.Surname == "Пушкин") < break; >Console.WriteLine(currPerson.FioInitials); >
Комментарии
Самоучитель по C# для начинающих. 02. Функции, классы, обьекты, коллекции — Комментарии (22)
chaser говорит 17.09.2013 в 4:30 :
Код в конце 2.2 можно продолжать усовершенствовать,
1) для начала отделить GUI(WriteLine()) от бизнес логики (Person)
2) избавляться от зависимостей по принципам SOLID
3) дойти до одного из паттернов MVC или MVVM
4) придти к IoC
Ведомир говорит 17.09.2013 в 12:42 :
Самое забавное в том, что информации по всему этому в сети гораздо больше, чем вот таких вот примитивных руководств для самых начинающих. Бумажные книги наоборот перегружены чудовищным количеством совершенно ненужной информации и пересказом msdn. И сам не так уж давно с нуля осваивал, и со стороны много раз видел с каким скрипом даются первые шаги. Но все иллюстрации продвинутых шаблонов очень плохо работают вне реального мира, они не объясняют их реальной сути и в итоге человек либо просто их отбрасывает как непонятные и сложные либо вынужденно применяет не понимая зачем. Я конечно не имею в виду те счастливые ситуации когда есть опытный программист-наставник и реальный проект на основвании которого это все можно посмотреть с обьяснениями.
я не иван иванов говорит 11.04.2014 в 10:35 :
Спасибо за труд, полезная для новичков (меня) информация )
Плут говорит 28.04.2014 в 23:40 :
public Person(string surname, string name, string otchestvo)
Name = name;
Surname = surname;
Otchestvo = otchestvo;
>
Поясни, пожалуйста этот пункт, примера, что здесь делается? Только начинаю учиться, поэтому частенько теряюсь:)
Плут говорит 28.04.2014 в 23:43 :
Плут:
Первого примера пункта 2.2. Классы и объекты
Ведомир говорит 29.04.2014 в 9:21 :
Описывается конструктор класса, функция которая вызывает при создании экземпляра класса. Она принимает три переменные и записывает их в свойства класса. Конструкторы экземпляров (Руководство по программированию в C#) Конструкторы (Руководство по программированию на C#)
Плут говорит 05.05.2014 в 17:51 :
public string FioInitials
get
string fio = Surname + " " + Name.Substring(0, 1) + ". " + Otchestvo.Substring(0, 1) + ".";
return fio;
>
>
Спасибо, ха предыдущий пункт, все стало понятно! Есть еще вопрос, по второму примеру, а, в этом блоке кода свойсвто гет зачем?
Плут говорит 11.05.2014 в 21:08 :
Плут:
public string FioInitials < get < string fio = Surname + " " + Name.Substring(0, 1) + ". " + Otchestvo.Substring(0, 1) + "."; return fio; >>
Спасибо, ха предыдущий пункт, все стало понятно! Есть еще вопрос, по второму примеру, а, в этом блоке кода свойсвто гет зачем?
Понятно, что он нужен, но не понятно что он делает
Ведомир говорит 12.05.2014 в 11:21 :
Гуглится за пару секунд - Свойства (Руководство по программированию в C#)
Виталий говорит 23.02.2015 в 17:31 :
Для чего нам нужно вот это в первом примере программы с классами? Ведь даже удалив эти строчки программа будет работать точно также.
System.Console.WriteLine(Person.GetClassDescription());
---------------------------------------
public static string GetClassDescription()
return "Класс Person. Хранит данные о человеке.";
>
Ведомир говорит 24.02.2015 в 10:15 :
Смысл всех тестовых примеров не в том, чтобы они что-то делали, а в том, чтобы они иллюстрировали описанное в статье. В данном случае иллюстрируется отличие статической функции от обычных функций и свойств, в том числе с синтаксическим сахаром.
Vacuit говорит 17.05.2015 в 18:04 :
2.5
int[] intArray = new int[5];
int[,] multiDimensionalIntArray = new int[2, 3]; Подскажите пожалуйста, для чего эти 2 строки. Код вроде и так работает. PS: Огромное спасибо за Вашу работу. Это лучшее пособие для начинающих, из тех что я встречал.
Ведомир говорит 17.05.2015 в 22:19 :
Просто показывают как создаются одно и много мерные массивы на самом простом примере. Практического смысла весь код из всех трех примеров не имеет - он нужен только для демонстрации.
Shadi говорит 09.07.2015 в 15:01 :
Очень интересно и грамотно написано. Спасибо автору за труд.
Дмитрий говорит 21.12.2015 в 14:16 :
Спасибо за отличный самоучитель! Все доступно и понятно. Автору огромное уважение!
Яна говорит 12.02.2016 в 17:26 :
Мне,как человеку,который начинает все с абсолютного нуля очень многое не понятно. Например,написание функции. тут приведен просто пример готовой ф-ции. но нету шаблона,чтоб можно было разобраться во всем. ибо не понятно,откуда автор все берет.
Официальная работа на дому с обучением.
Олег говорит 16.01.2018 в 18:41 :
Спасибо за труд. Очень хотелось бы, чтобы подробнее объяснили что именно вы написали в п.2,5 (т.е. подробнее о многомерных массивах и листах)
Никогда не сдавайся.
Работа в интернете
Сергей говорит 25.01.2019 в 18:39 :
Почему никого не смутило personsArray.Count() ?
Такой метод применим только к массивам, приведенным к виду ICollection.
Чтобы вывести длину массива необходимо использовать personsArray.Lenght
Сергей говорит 17.05.2019 в 18:41 :
Немного непонятно про функцию.. мало пояснений.. например почему в первом примере "Уродливый код" не показан в виде:
System.Console.WriteLine(surname + " " + name + " " + otchestvo);
System.Console.WriteLine(surname + " " + name.Substring(0, 1) + "." + otchestvo.Substring(0, 1) + ".");
Как того просит задание, вывести на экран фамилию с инициалами?
Потом почему переменной surname2 присвоено значение но используется surname и при этом выводится на экран значение surname2?
Как вызвать функцию в функции
Как вызвать функцию из функции?
Мб не туда мысли пошли, но хочу передать метод аргументом в метод, т.е. чтобы не писать проверку.
Как вызвать функцию из функции
Всем привет . Не могу понять как вызвать функцию из функции adea.game.prototype = < update.
Как вызвать функцию из функции main?
работаю в деве. предположим я создал проект, изначально в нем находится файл main.cpp. предположим.
Регистрация: 01.04.2011
Сообщений: 20
та вот так прямо и вызвать
file(zep, raz);
Регистрация: 23.01.2012
Сообщений: 449
Сообщение от chipmunk
та вот так прямо и вызвать
Не получается ругается на file( zep , raz );
719 / 710 / 168
Регистрация: 15.06.2011
Сообщений: 1,704
Сообщение от sasha0192
Не получается ругается на file( zep , raz );
Естевственно, нужно посылать в функцию переменные!
Функция ждет 2 строки - значит и шлем 2 строки.
file("Строка_1", "Строка_2");
Регистрация: 23.01.2012
Сообщений: 449
Сообщение от DimanRu
Естевственно, нужно посылать в функцию переменные!
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
private void Copi(string html, string path) // Скачка файла из интернета { WebClient webClient = new WebClient(); webClient.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); string downloadFileName = System.IO.Path.GetFileName(html); webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged); webClient.DownloadFileAsync(new Uri(html), path + downloadFileName); webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed); //После скачки файла переходим на [B]Completed[/B] } private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e) //Отображение Prograssbara { progressBar1.Value = e.ProgressPercentage; label1.Text = "" + e.ProgressPercentage + ""; label4.Text = "Идет загрузка"; } private void Completed(object sender, AsyncCompletedEventArgs e) { //тут код который переадресовывает в [B]ziip[/B] } private void ziip(string zep, string raz) //Распаковываем архив { double p = double.Parse(label1.Text); try { label4.Text = "Идет разархивация"; string create = zep; string patch = raz; using (ZipFile zip = ZipFile.Read(create)) { zip.ProvisionalAlternateEncoding = Encoding.UTF8; zip.ExtractAll(patch); progressBar1.Value = 0; progressBar1.Value = Convert.ToInt32(100); // label4.Text = "Разархивация завершина"; } } catch (Exception) { label4.Text = "Ошибка кода"; } } private void button4_Click(object sender, EventArgs e) { string html = txtUrl.Text; // Http:/адрес прямой ссылки на скачку string path = txtPath.Text; // Указание путив которую будет производится загрузка Copi(html, path); string zep = @"D:\es\rzip.zip"; //Расположение ZIP архива string raz = txtPath.Text; //Путь распаковки }
Который работает так:
0) Нажимаем на кнопку button4_Click в который указываем пути (присваиваем значения html, path, zep, raz)
1) Скачиваем файл с интернета
1.1) выводим в PrograssBar процесс загрузки
1.2) После скачки файла идет обращение в Completed
1.3) Completed переадресовывает на ziip
2)Идет распаковка архива
719 / 710 / 168
Регистрация: 15.06.2011
Сообщений: 1,704
Вот в этих строках:
string raz = txtPath.Text; //Путь распаковки string path = txtPath.Text; // Указание путив которую будет производится загрузка
Текст в txtPath должен быть вида "с:\\file.txt", а если в поле вводится путь с одиночными слешами, следует писать:
string raz = @txtPath.Text; //Путь распаковки string path = @txtPath.Text; // Указание путив которую будет производится загрузка
А проблемы с вызовом у вас в том, что нужные переменные объявлены в другом методе, и недоступны там где вызывается функция. Объявите string raz и string zep в классе, и тогда они будут доступны.
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
private void Copi(string html, string path) // Скачка файла из интернета //Объявляем переменнные string zep; string raz; . { WebClient webClient = new WebClient(); webClient.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); string downloadFileName = System.IO.Path.GetFileName(html); webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged); webClient.DownloadFileAsync(new Uri(html), path + downloadFileName); webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed); //После скачки файла переходим на [B]Completed[/B] } private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e) //Отображение Prograssbara { progressBar1.Value = e.ProgressPercentage; label1.Text = "" + e.ProgressPercentage + ""; label4.Text = "Идет загрузка"; } private void Completed(object sender, AsyncCompletedEventArgs e) { //тут код который переадресовывает в [B]ziip[/B] } private void ziip(string zep, string raz) //Распаковываем архив { double p = double.Parse(label1.Text); try { label4.Text = "Идет разархивация"; string create = zep; string patch = raz; using (ZipFile zip = ZipFile.Read(create)) { zip.ProvisionalAlternateEncoding = Encoding.UTF8; zip.ExtractAll(patch); progressBar1.Value = 0; progressBar1.Value = Convert.ToInt32(100); // label4.Text = "Разархивация завершина"; } } catch (Exception) { label4.Text = "Ошибка кода"; } } private void button4_Click(object sender, EventArgs e) { string html = txtUrl.Text; // Http:/адрес прямой ссылки на скачку string path = @txtPath.Text; // Указание путив которую будет производится загрузка Copi(html, path); zep = @"D:\es\rzip.zip"; //Расположение ZIP архива raz = @txtPath.Text; //Путь распаковки }