Я часто бывал на собеседованиях, но некоторые вопросы ставили меня в тупик, кстати, и запоминались они почему-то после собеседований. Поэтому приходя домой, я обязательно искал ответы на них. Вот некоторые вопросы, которые мне приходилось слышать на собеседовании по темам “Делегаты” и “Потоки”. Я приведу свои ответы. Возможно они не во всём правильные, было бы интересно подискуссировать с вами на эти темы. Жду ваших комментариев J
Делегаты и события.
- Переделайте пример так, чтобы он работал только с параметрами типа int(Я переделывал наоборот из int во float, ниже мой ответ и одновременно задание для вас).
class Program { public delegate float TypeSafeAddFunctionDelegate(float x, float y); public static float Add(float x, float y) { Console.WriteLine(x+y); return x + y; } static void Main() { float z; TypeSafeAddFunctionDelegate f = new TypeSafeAddFunctionDelegate(Add); z = Add(13.2f, 17.2f); z = f(13.2f, 17.2f); } } |
- Создайте консольное приложение, которое вызывает функцию EnumWindows из Win32 API и перебирает все окна верхнего уровня.
public delegate bool CallBackDelegate(int hwnd, int lParam); public class Program { [DllImport("user32")] public static extern int EnumWindows(CallBackDelegate x, int y); public static void Main() { CallBackDelegate myCallBack = new CallBackDelegate(Report); EnumWindows(myCallBack, 0); } public static bool Report(int hwnd, int lParam) { Console.Write("Window handle is "); Console.WriteLine(hwnd); return true; } } |
- Придумайте хотя бы два примера использования делегатов.
- Делегаты используются по двум основным причинам:
2) Делегаты позволяют во время выполнения программы выполнить метод, который точно не известен во время компиляции.
§ Делегаты наделены возможностью группового вызова. Также делегаты могут приниматься в виде параметров в функции. Следующий пример демонстрирует это.
class Dog { //Имя собаки private string name; public string Name { get { return name; } set { name = value; } } //Грязная ли собака? private bool isDirty; public bool IsDirty { get { return isDirty; } set { isDirty = value; } } //Привитая ли собака? private bool isInoculated; public bool IsInoculated { get { return isInoculated; } set { isInoculated = value; } } //Конструктор public Dog(string name, bool isDirty, bool isInoculated) { this.name = name; this.isDirty = isDirty; this.isInoculated = isInoculated; } //Делегат.Может вызывать любой метод, получающий объект Dog //в виде параметра и не возвращающий ничего public delegate void DogDelegate(Dog d); } |
//Собачий питомник class Kennel { //Список собак в питомнике ArrayList theDog = new ArrayList(); //Конструктор public Kennel() { theDog.Add(new Dog("Tuzik",true,false)); theDog.Add(new Dog("Lessy",true,true)); theDog.Add(new Dog("Rex",false,true)); } //Ветеринарный осмотр public void VeterinaryExamination(Dog.DogDelegate exam) { foreach (Dog d in theDog) { Console.WriteLine("Осмотр собаки "+d.Name); exam(d); } } } |
//Ветеринарный пункт class VeterinaryStation { //Моем собаку public void WashDog(Dog d) { if (d.IsDirty) Console.WriteLine("Моем собаку"); else Console.WriteLine("Собака уже помыта"); } //Прививаем собаку public void InoculateDog(Dog d) { if (d.IsInoculated) Console.WriteLine("Делаем прививку"); else Console.WriteLine("Прививка уже сделана"); } } |
class Program { static void Main(string[] args) { Kennel kennel = new Kennel(); VeterinaryStation station = new VeterinaryStation(); //Осмотр собак kennel.VeterinaryExamination(new Dog.DogDelegate(station.WashDog)); kennel.VeterinaryExamination(new Dog.DogDelegate(station.InoculateDog)); Console.ReadLine(); } } |
class Program { delegate void BynaryOp(string x, string y); static void Add(string x, string y) { int sum =Int32.Parse(x) + Int32.Parse(y); Console.WriteLine("Сумма = " + sum); } static void Subtract(string x, string y) { int sub = Int32.Parse(x) - Int32.Parse(y); Console.WriteLine("Разность = " + sub); } static void Main() { //Если ввести некорректные x и y, то будет сгенерировано //исключение в методах на которые указывает делегат string x=Console.ReadLine(); string y = Console.ReadLine(); BynaryOp del = new BynaryOp(Add); del += new BynaryOp(Subtract); foreach (BynaryOp d in del.GetInvocationList()) { // Обернем вызов функции в защищенный // блок, что позволит предотвратить // преждевременное завершение алгоритма. try { d(x, y); } catch (Exception ex) { Console.WriteLine("Some exception has occurred"); } } } } |
- Подумайте, какие из следующих утверждений, касающихся событий в C#, верны, а какие – нет.
B. События не могут нести контекстной информации вместе с собой. – не верно.
C. События нотифицируют подписчиков в том случае, если что-то произошло.- верно, нотифицируют , если произошло это событие.
D. Публикатор события определяет, когда событие будет вызываться.- верно.
E. События базируются на делегатах.- верно. Обработчики соытий представляются делегатами..
- Обсудите преимущества использования событий.
- При обработке ключевого слова event компилятор автоматически создаёт для вас методы регистрации, а также члены переменные, необходимые для типа делегата.
- Использование событий упрощает регистрацию обработчиков событий вызывающей стороны.
- При обработке ключевого слова event компилятор автоматически создаёт для вас методы регистрации, а также члены переменные, необходимые для типа делегата.
- Каким из следующих способов можно запустить поток newProcess?
B. newProcessEntry.Start(); - не верно. Нет такого метода.
C. newProcess.Start(newProcessEntry); - не верно. Не верный параметр.
D. newProcessEntry.Run(); - не верно. Нет такого метода.
E. newProcess.Run(); - не верно. Нет такого метода.
- Напишите программу, создающую три потока одного типа. Каждому из потоков в параметре конструктора должна передаваться строка с его именем (например, «Первый»), затем он должен прокручиваться в цикле n раз (n задается в параметрах командной строки) и завершать свою работу. При каждом проходе цикла поток должен печатать номер прохода цикла и свое имя, затем засыпать на какой-то промежуток времени и продолжать выполнение. При завершении поток должен печатать строку следующего формата: «Поток <Имя> завершен».
class Program { static private int n; static void Main(string[] args) { if (args.Length == 0) { return; } else { n=Int32.Parse(args[0]); } Thread thread1 = new Thread(func); Thread thread2 = new Thread(func); Thread thread3 = new Thread(func); thread1.Start("Thread1"); thread2.Start("Thread2"); thread3.Start("Thread3"); } static void func(object name) { Random rand = new Random(); Thread.CurrentThread.Name=(string)name; for (int i = 0; i < n; i++) { Console.WriteLine("Номер цикла " + n +","+name); Thread.Sleep(rand.Next(1000)); } Console.WriteLine("Поток "+ name+"завершён."); } } |
- Что такое «критическая секция» в терминах многопоточности?
- Критические секции - это объекты, используемые для блокировки доступа всех нитей (threads) приложения, кроме одной, к некоторым важным данным в один момент времени.
- Приведите хотя бы два примера задач, в которых фигурирует критическая секция.
- Чтение/запись в файл
- Использование принтера
- Чтение/запись в файл
- Каким образом можно выделить критическую секцию в C#?
Monitor.Enter(this); m_dwSmth = dwSmth; Monitor.Exit(this); |
- Какие из следующих способов использования lock являются правильными?
public void InsertData()
{
try
{
lock{_cmdBCP.ExecuteNonQuery();}
}
catch
{
System.Console.WriteLine("Caught an exception");
}
finally {
unlock;
}
}
B. Неверно
public void InsertData()
{
try
{
lock(this) {_cmdBCP.ExecuteNonQuery();}
}
catch
{
System.Console.WriteLine("Caught an exception");
}
finally
{
unlock(this);
}
}
C. Неверно
public void InsertData()
{
try
{
lock(this) {_cmdBCP.ExecuteNonQuery();}
}
catch
{
System.Console.WriteLine("Caught an exception");
}
finally
{
lock.Exit(this);
}
}
D.Верно
public void InsertData()
{
try
{
lock(this) {_cmdBCP.ExecuteNonQuery();}
}
catch
{
System.Console.WriteLine("Caught an exception");
}
}
E. Неверно
public void InsertData()
{
try
{
lock(_cmdBCP.ExecuteNonQuery())
}
catch
{
System.Console.WriteLine("Caught an exception");
}
finally
{
unlock();
}
}
- Обсудите преимущества и недостатки использования многопоточности.
- Недостатки:
· Чрезмерное использование многопоточности отнимает ресурсы и время CPU на создание потоков и переключение между потоками.
· Увеличивается вероятность возникновения трудноуловимых ошибок в программе.
§ Достоинства:
· Методы выполняться быстрее, если рабочая нагрузка разнесена по нескольким потокам.
· Возможность выполнять длительные вычисления в фоновом режиме.
· При выполнении вычислений в фоновом режиме, приложение не выглядит зависшим и способно реагировать на запросы клавиатуры, мыши или на др. действия.
Обязательные правила любого программиста
И напоследок несколько обязательных правил, которые я считаю нужно везде и всегда исполнять, даже, особенно на собеседовании, когда вас просят сделать тестовое задание или написать кусок кода. В первую очередь они смотрят на стиль вашего кодирования, на аккуратность и понятность кода. Поэтому думая над решением задачи не забывайте об основных правилах:
· Код нужно комментировать, используя XML-комментарии.
http://msdn2.microsoft.com/en-us/library/b2s063f7(vs.71).aspx
· Каждый класс должен быть в отдельном файле, название которого должно совпадать с названием класса. То же самое касается интерфейсов и структур.
Другое именование приведет к серьезной путанице на большом проекте в команде.
· Прежде чем сдавать задания на проверку, убедитесь, что проект компилируется и запускается, что все пункты ТЗ выполнены.
· Приложение не должно выбрасывать необработанные исключения.
По поводу этого правила обязательно почитайте Рихтера, глава 18.
Если вы отображаете сообщение об исключении пользователю, то оно должно быть понятным ему, а также содержать указания (явно или неявно), что нужно сделать, чтобы исключение не возникало.
- Память должна освобождаться корректно для неуправляемых ресурсов.
- Необходимо разделять логику и интерфейс.
- В каждом приложении должна быть реализована защита от дурака (если ожидаем, что пользователь введет целое число, а пользователь ввел строку, приложение должно не упасть, а корректно обработать данную ситуацию).
- Если функция получается громоздкой (больше экрана), необходимо разделить ее на несколько функций по смыслу.
- Все решения нужно писать в соответствии с ООП.
- Необходимо избегать дублирования в коде, за Copy/Past бывает расстрел на месте J
Иерархию классов нужно прорабатывать так, чтобы между классами не было никакого дублирования, то есть всё общее нужно выносить в базовые абстрактные классы
Вот, пожалуй, самое основное, что я всегда помню и соблюдаю. Хочу поблагодарить школу программирования компании “Epam Systems”, которая в своё время заставила меня это заучить на всю жизнь.
> Критические секции - это объекты, используемые для блокировки доступа всех нитей (threads) приложения, кроме одной, к некоторым важным данным в один момент времени.
ОтветитьУдалитьммм... это скорее определение мьютекса.
Критическая секция - это участок кода во время исполнения которого доступ к одному объекту может произойти из нескольких потоков исполнения.
Соотв. из-за этого могут происходить ошибки(например два потока пытаются одновременно читать из файла или писать в него - нехорошо получится).
п.с. могу даже дать ссылку на литературу:
ОтветитьУдалитьД.В. Иртегов "Введение в операционные системы" стр. 378
даётся опр. КС - это интервал, в течение которого модификация нарушает целостность разделяемой структуры данных, и, наоборот, интервал, в течение которого алгоритм нити полагается на целостносить этой структуры.
а достигается целостность структуры данных уже за счёт различных объектов реализованных в коде(мьютексы, семафоры, мониторы и т.д.)