Змінюй хід війни! Допомагай ЗСУ!

C# не легкая это работа....

🔴 21:26 Повітряна тривога в Харків.обл.
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #41
А в книгах почему-то написано что есть.

Если увидишь книгу в которой написано что в C# есть деструкторы, смело кидай ее в печку, на большее она не годится :)

В C# нет деструкторов, есть финалайзер, причем он имеет ряд ограничений на использование - из финалайзера можно обращаться только к unmanaged ресурсам (windows handle, различные дескрипторы и т.п.). Больше ничего из финалайзера делать нельзя. К тому-же он вызывается из специального потока GC, его вызов не гарантирован и время выполнения ограничено (выполнение будет принудительно прервано после истечения нескольких секунд). Предназначен финалайзер для того чтобы GC имел возможность почистить хендлы. Если ты не используешь win api interop, то финалайзер создавать не следует! ;)
Чтобы было понятнее, финалайзер нужен для GC. На логику программы его удаление никак не должно влиять. Обращение к элементам класса из финалайзера является грубой ошибкой.

Вместо деструкторов в C# используется паттерн Dispose, для чего в основной библиотеке есть интерфейс IDisposable. C# поддерживает этот паттерн на уровне языка, например есть блоки using для работы с объектами реализующими этот паттерн. Паттерн Dispose не имеет отношения к финализатору - это разные вещи.
 
Останнє редагування:
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #42
Klez, я абсолютно понимаю что это разные вещи.
А теперь смотрите что произошло:
У меня три книги в которой описаны деструкторы. В первой (это учебный курс) есть примеры с консолью, и всё работало как и описано (но т.к. книга для начинающих то ничего сложного там и не было пототому и работало). Во второй о них написано мало, но я счёл это логичным т.к. книга для чуть более подготовленного уровня. Вот тут я и споткнулся - попытки завершить мой поток в деструкторе формы приводили к непонятной ошибке. Тут вы пишите что деструкторов нету, а есть финализаторы. Я открываю 3-ю книгу. Там тоже есть деструкторы, но написано вот что:
Хотя мы говорим о деструкторах в C#, в лежащей в его основе архитектуре NET они известны как финализаторы. Когда вы определяете деструктор на C#, компилятор помещает в сборку метод под названием Finalize(). Это никак не влияет на ваш исходный код, но вам следует иметь в виду это обстоятельство..... Например
class MyClass
{
~MyClass()
{
// реализация деструктора
}
}
Когда компилятор C# компилирует деструктор, он неявно транслирует его код в эквивалентный метод Finalize()

protected override void Finalize()
{
try
{
// реализация деструктора
}
finally
{
base.Finalize();
}
}
и далее про проблемы применения деструкторов в C# и про Dispose.
Теперь мне кое что ясно, я успешно завершил поток в Dispose... Спасибо за наводку(а кстати, зачем это пихать в Close() советовали? Это имеет смысл?)

з.ы. что такое GC и "win api interop" ? :)
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #43
Вот тут я и споткнулся - попытки завершить мой поток в деструкторе формы приводили к непонятной ошибке.

метод с именем из символа ~ и имени класса - это не деструктор, а финализатор. Это особый метод из него нельзя завершать поток, и делать другие подобные вещи тоже нельзя. Из этого метода можно вызвать API'шный вызов CloseHandle и т.п., но не более!
Если ты не работаешь с хэндлами и win api интеропом, то финализатор делать не нужно!

Теперь мне кое что ясно, я успешно завершил поток в Dispose... Спасибо за наводку(а кстати, зачем это пихать в Close() советовали? Это имеет смысл?)

Совершенно верно, это следует делать в методе Dispose, но не следует забывать что метод Dispose нужно вызывать самому! ;)
Чтобы не забыть его вызвать, можно использовать блок using, например:

Код:
using(Form form=new Form())
{
    form.Show();
    MessageBox.Show(form.DialogResult.ToString());
}

в данном случае компилятор сам расставит вызов form.Dispose(), везде где код покидает блок using, в том числе и в случае исключений.

Close - это альтернатива методу Dispose. Просто Dispose это стандартный для C# метод, а Close может комуто просто более удобнее вызывать, обычно это одно и то-же
Если объект реализует интерфейс IDisposable, то после его использования нужно не забывать вызывать Dispose() или поместить его в блок using


з.ы. что такое GC и "win api interop" ? :)

GC - это Garbage Collector (сборщик мусора), специальный поток который время от времени очищает память занятую объектами которые уже не нужны.

интероп - вызов функций из unmanaged библиотек, например функций Win API. Дело в том что выделение и освобождение памяти в дотнет среде и в unmanaged коде происходит по разному, это нужно учитывать. Интероп это вызов кода в другой среде, для того чтобы произвести вызов правильно, нужно описать аргументы, указать как их нужно маршалить, учесть особенности выделения памяти и т.п.
 
Останнє редагування:
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #44
Что такое GC просто не угадал. Просьба: когда отвечаете новичкам старайтесь не применять сокращений :пиво:
До интероп ещё недочитал.
метод с именем из символа ~ и имени класса - это не деструктор, а финализатор. Это особый метод из него нельзя завершать поток, и делать другие подобные вещи тоже нельзя. Из этого метода можно вызвать API'шный вызов CloseHandle и т.п., но не более!
Если ты не работаешь с хэндлами и win api интеропом, то финализатор делать не нужно!
Вот здесь нужны подробности, где почитать почему нельзя. Т.к. знать поверхностно это не дело! В будущем будут и хэндлы и win api... всё будет ;)
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #45
Вот здесь нужны подробности, где почитать почему нельзя.

Из Рихтера:
"Опытные разработчики на C++ интенсивно используют деструкторы — иногда не только для того, чтобы очищать ресурсы, но также, чтобы предоставлять отладочную информацию и выполнять другие задачи. Деструкторы С# используются гораздо реже, чем их эквиваленты в C++. Проблема с деструкторами С# по сравнению с их аналогами из C++ состоит в том, что они недетерминированы. Когда уничтожается объект C++, его деструктор запускается немедленно. Однако, из-за способа работы сборщика мусора, нет возможности узнать, когда на самом деле будет вызван деструктор объекта. А потому вы не можете поместить в него никакой код, полагающийся на запуск в определенное время, а также не можете рассчитывать, что деструкторы
разных экземпляров классов будут вызваны в каком-то определенном порядке. Когда ваш объект удерживает некоторые критичные ресурсы, которые необходимо освободить как можно скорее, вы не можете ждать сборки мусора. Другая проблема с деструкторами С# связана с тем, что их реализация задерживает финальное удаление объекта из памяти. Объекты, которые не имеют деструкторов, удаляются из памяти за один проход сборщика мусора, но те объекты, у которых есть деструкторы, требуют для своего уничтожения двух проходов: первый вызывает деструктор без уничтожения объекта, а второй — уничтожает его. Вдобавок к этому исполняющая система использует единственный поток для выполнения методов Finalize () всех объектов. Если вы часто используете деструкторы и применяете их для выполнения долго идущих задач, влияние на производительность может стать ощутимым.
"
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #46
Проще для начала ТС взять книжку по Делфи или С++ основы программирования, потом уже и по ООП что то читать, да и Страуструпа не помешало бы полистать.
В свое время тоже заморачивался на с#, но не осилил, уж больно привык к с++ и делфям с ручной работой с памятью, указателями и т.п. да и с++ пригодился когда перешел на QNX, тут только на нем и работаю. С# - это завязка на виндах, ни шагу в сторону (хотя читал что вроде бы и под линукс нет есть, но не щупал)...
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #47
Вот здесь нужны подробности, где почитать почему нельзя. Т.к. знать поверхностно это не дело! В будущем будут и хэндлы и win api... всё будет ;)

просто не делай так и все, чтобы понять почему - нужно хорошо знать как работает GC, а это на несколько порядков сложней чем то-же выделение памяти в C++...
Если есть желание все-же разобраться, у Рихтера об это вполне доступно написано
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #48
Klez
:D Так у Рихтера же есть деструкторы в C#, следовательно его надо в мусор по вашим же рекомендациям!

2 Fregl
ТС-у может и проще, незнаю. Но мне точно нет, C# проще в изучении.
Да, завязка на винду, чем плохо? Всё равно это программирование :клас:

2 Kvest
Вот вы привели цитату. Не заметили что в моём посте цитата из той же книги? :) Я это всё прочитал. Это не обьясняет, почему я не могу остановить поток из деструктора. Мне не надо было освобождать какие-то ресурсы как можно быстрее, я спокойно хотел дождаться GC, тем более что обьект (форма) не уничтожается за 1 проход, а поток только и делал что рисовал на форме примитивы :D , так что мне нужно ещё более подробно почему этого делать нельзя. Там дальше расписано, или есть какая-то другая книга Рихтера?
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #49
Там дальше расписано, или есть какая-то другая книга Рихтера?

Неугомонный какой :), ну, на сколько я знаю других книг по CLR у Рихтера нет, может есть более новая версия этой, но я сомневаюсь, т.к. она не старая, за 2007г.
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #50
Я это всё прочитал. Это не обьясняет, почему я не могу остановить поток из деструктора.

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

Короче останавливать поток который уже остановлен и уничтожен бессмысленно
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #51
Klez
Всё было наоборот! Я закрываю форму, но экземпляр Thread висит в памяти и работает! Это совершенно точно, хотя он был и определён и запущен прямо из экземпляра формы, а не с другого обьекта. И метод который он выполнял тоже разумеется был определён в форме. Прикольно, да? Форма закрылась по dispose, а поток РАБОТАЕТ :confused:
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #52
Klez
Всё было наоборот! Я закрываю форму, но экземпляр Thread висит в памяти и работает!

финализатор вызывается тогда, когда GC нужно почистить память, в реальности это происходит очень редко или когда в приложении заканчивается память или при закрытиии приложения, или (что чаще всего и происходит) - никогда. Кроме того, как я уже писал - финализатор будет вызван когда экземпляр Thread внутри твоего класса перестанет существовать ;)
Еще раз подчеркиваю финализатор - это не деструктор, и у Рихтера об этом довольно подробно описано и уделяется этому моменту особое внимание. Финализатор не предназначен для управления логикой программы, он предназначен для очистки unmanaged ресурсов! Если ты не используешь интероп, то ты не используешь unmanaged ресурсы, поэтому финализатор имплементировать не надо!

Обрати внимание, в книгах Рихтера например, есть examples - для объектов с финализатором крайне желательно реализовывать паттерн Dispose, так вот, обрати внимание что если вызов Dispose инициирован финализатором, то вся работа по очистке managed объектов игнорируется - выполняется только освобождение unmanaged ресурсов. А вот если вызов был произведен из кода, тогда вся работа по очистке managed объектов выполняется...

Что ты хотел останавливать из финализатора, если в момент его вызвова все уже остановлено и уничтожено??! :confused:

Прикольно, да? Форма закрылась по dispose, а поток РАБОТАЕТ :confused:

и что тут странного? так и должно быть, твой поток очевидно не background, поэтому при завершении основного потока приложения (в том числе и остановки цикла прокачки сообщений), твой дополнительный поток будет продолжать работать. Почему он собственно должен завершаться?! Если бы он завершался - это уже был бы баг...
 
Останнє редагування:
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #53
финализатор вызывается тогда, когда GC нужно почистить память, в реальности это происходит очень редко......
да, уже провёл эксперимент и понял это. Слишком редко :(

для объектов с финализатором крайне желательно реализовывать паттерн Dispose, так вот, обрати внимание что если вызов Dispose инициирован финализатором, то вся работа по очистке managed объектов игнорируется - выполняется только освобождение unmanaged ресурсов. А вот если вызов был произведен из кода, тогда вся работа по очистке managed объектов выполняется...
вот, тоже интересно разобрать почему так происходит.

Klez сказав(ла):
твой поток очевидно не background, поэтому при завершении основного потока приложения (в том числе и остановки цикла прокачки сообщений), твой дополнительный поток будет продолжать работать.
Но он будет уничтожен, когда наступит сборка мусора, так? У меня она просто не наступала при закрытии формы :( а я думал что сам деструктор неработает.

Кстати, всё же деструкторы в C# есть! Посилання видалено Будеш и после этого настаивать? ;)
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #54
та скоко ж можно в самом деле
Supostat, прекращай эту деструктивную деятельность :D

For example, the following is a declaration of a destructor for the class Car:

class Car
{
~ Car() // destructor
{
// cleanup statements...
}
}




The destructor implicitly calls Finalize on the base class of the object. Therefore, the previous destructor code is implicitly translated to the following code:

protected override void Finalize()
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}

зря, зря обозвали его ~Класснейм() ...
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #55
С# - это завязка на виндах, ни шагу в сторону (хотя читал что вроде бы и под линукс нет есть, но не щупал)...

дотнет есть и под windows и под линукс и под юникс и на мобильных телефонах тоже есть :) И вполне нормально работает, но на windows - это бесспорно конфета :)

Кстати, всё же деструкторы в C# есть! Посилання видалено Будеш и после этого настаивать? ;)

Да, нету в C# деструкторов, какойто умнит назвал финализатор деструктором и морочит голову людям, которые услышав слово деструктор пытаются его использовать как деструктор и получают граблями по лбу. Я думаю ты уже прекрасно понимаешь что это совсем не тот деструктор что в C++, поэтому не стоит использовать терминологию вводящую людей в заблуждение. Это правильно называть финализатором.
Сходство с деструктором только в синтаксисе и я совершенно согласен - не стоило задавать для финализатора синтаксис деструктора из C++, это только создает путаницу.

p.s.: не ты первый и не ты последний кто по ошибке принмает финализатор за деструктор в дотнете... Через эти грабли почти все проходят :)
 
Останнє редагування:
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #56
Я вижу что метод ~Classname() назвали деструктором. И в С++ и в C# (финализатором в .NET но не в С#). Граблями получают по лбу потому что механизм их вызова отличается. Это важный нюанс! Когда деструктор в C++ выполняется? Сразу как обьект выходит из области видимости и на него нет ссылок?
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #57
Когда деструктор в C++ выполняется?
При уничтожении объекта с помощью операции delete (в случае если ты его разместил в динамической памяти), или
Сразу как обьект выходит из области видимости
в случае когда он был размещен в стэке

Klez
Всё было наоборот! Я закрываю форму, но экземпляр Thread висит в памяти и работает! Это совершенно точно, хотя он был и определён и запущен прямо из экземпляра формы, а не с другого обьекта. И метод который он выполнял тоже разумеется был определён в форме. Прикольно, да? Форма закрылась по dispose, а поток РАБОТАЕТ :confused:

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

И кстати, что делает твой поток?
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #58
хочу выложить свою первую программу, после Hello world конечно.
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Carto;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.ArcMapUI;

namespace CommandInheritingBaseCommand
{
/// <summary>
/// Summary description for ZoomToLayer.
/// </summary>
[Guid("3d2e52d7-c788-4e00-9d3c-3e3ba838e567")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("CommandInheritingBaseCommand.ZoomToLayer")]
public sealed class ZoomToLayer : BaseCommand
{
#region COM Registration Function(s)
[ComRegisterFunction()]
[ComVisible(false)]
static void RegisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryRegistration(registerType);

//
// TODO: Add any COM registration code here
//
}

[ComUnregisterFunction()]
[ComVisible(false)]
static void UnregisterFunction(Type registerType)
{
// Required for ArcGIS Component Category Registrar support
ArcGISCategoryUnregistration(registerType);

//
// TODO: Add any COM unregistration code here
//
}

#region ArcGIS Component Category Registrar generated code
/// <summary>
/// Required method for ArcGIS Component Category registration -
/// Do not modify the contents of this method with the code editor.
/// </summary>
private static void ArcGISCategoryRegistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
MxCommands.Register(regKey);

}
/// <summary>
/// Required method for ArcGIS Component Category unregistration -
/// Do not modify the contents of this method with the code editor.
/// </summary>
private static void ArcGISCategoryUnregistration(Type registerType)
{
string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
MxCommands.Unregister(regKey);

}

#endregion
#endregion

private IApplication m_application;
public ZoomToLayer()
{
//
// TODO: Define values for the public properties
//
base.m_category = "govno"; //localizable text
base.m_caption = "xuge"; //localizable text
base.m_message = "gavna"; //localizable text
base.m_toolTip = "tak_emu"; //localizable text
base.m_name = "fefe"; //unique id, non-localizable (e.g. "MyCategory_ArcMapCommand")

try
{
//
// TODO: change bitmap name if necessary
//
string bitmapResourceName = GetType().Name + ".bmp";
base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
}
}

#region fuck

/// <summary>
/// Occurs when this command is created
/// </summary>
/// <param name="hook">Instance of the application</param>
public override void OnCreate(object hook)
{
if (hook == null)
return;

m_application = hook as IApplication;

//Disable if it is not ArcMap
if (hook is IMxApplication)
base.m_enabled = true;
else
base.m_enabled = false;

// TODO: Add other initialization code
}

/// <summary>
/// Occurs when this command is clicked
/// </summary>
public override void OnClick()
{
// TODO: Add ZoomToLayer.OnClick implementation
IMxDocument mxDocument = GetMxDocumentFromArcMap(m_application);

ZoomToActiveLayerInTOC(mxDocument);

}

#endregion


#region "Zoom to Active Layer in TOC"

///<summary>Zooms to the selected layer in the TOC associated with the active view.</summary>
///
///<param name="mxDocument">An IMxDocument interface</param>
///
///<remarks></remarks>
public void ZoomToActiveLayerInTOC(IMxDocument mxDocument)
{
if(mxDocument == null)
{
return;
}
IActiveView activeView = mxDocument.ActiveView;

// Get the TOC
IContentsView IContentsView = mxDocument.CurrentContentsView;

// Get the selected layer
System.Object selectedItem = IContentsView.SelectedItem;
if (!(selectedItem is ILayer))
{
return;
}
ILayer layer = selectedItem as ILayer;


// Zoom to the extent of the layer and refresh the map
activeView.Extent = layer.AreaOfInterest;
activeView.Refresh();
}
#endregion


#region "Get MxDocument from ArcMap"

///<summary>Get MxDocument from ArcMap.</summary>
///<param name="application">An IApplication interface that is the ArcMap application.</param>
///<returns>An IMxDocument interface.</returns>
///<remarks></remarks>
public IMxDocument GetMxDocumentFromArcMap(IApplication application)
{

if (application == null)
{
return null;
}

IDocument document = application.Document;
IMxDocument mxDocument = (IMxDocument)(document); // Explicit Cast

return mxDocument;

}
#endregion
}
}
Большую часть кода я содрал с примеров но сложил до кучи сам, хотя многие вещи мне до сих пор не понятны.
разработчик АПИ по аналогии с майкрософтом тоже ведет свой МСДН. На сайте обнаружил огромное колличество примеров, шаблонов и описания классов. Параллельно продолжаю изучать С шарп по самоучителю, прохожу видеокурсы на майкрософте + работа с шаблонами. Вот так собственно и живем.
Нашел у разработчиков свойский Object Browser пытаюсь читать. Что правда наткнулся на понятие coclass, надо почитать что это такое. Уже, кажись, разобрался с виртуальными методами и переопределение их по ссылке. Далее перегруженные операторы и подхожу к интерфейсам.

Чувство паники и разочарований, что я один такой тупой на всем белом свете, надеюсь уже позади. По крайней мере я вижу куда надо идти и где копать. Только бы не розтерять интерес и пыл к программированию - наверное, это самая большая опасность. спасибо.
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #59
неплохой пример, как не надо писать код :)
 
  • 🔴 21:26 Повітряна тривога в Харків.обл.
  • #60
страшный код! Кстати к интерфейсов ты как я понял не изучал еще (из того что ты написал), но юзаешь их вовсю
 
Назад
Зверху Знизу