Программирование PC Visual Studio C#: часто задаваемые вопросы (FAQ) Thu, January 19 2017  

Поделиться

нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Visual Studio C#: часто задаваемые вопросы (FAQ) Печать
Добавил(а) microsin   

В этой статье опубликованы ответы на часто появляющиеся вопросы при программировании в среде C# от Microsoft.

System.Console.WriteLine("Hello, World");. См. также:

Преобразование чисел, даты, времени в строку: Q014, Q015, Q016, Q019, Q054.

Преобразование строк в числа: Q022.

По новым правилам не нужно указывать ключевое слово void в параметрах для процедуры, если у неё отсутствуют параметры.

// CS1536.cs
class a
{ public static int x( void )
// эта строка вызовет ошибку CS1536 // Чтобы устранить ошибку, попробуйте определить функцию так: // public static int x() { return 0; }   public static void Main() { }
} 

String strVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); 

Такая ошибка возникала при двойном клике на файле солюшена *.sln. Если просто запустить Visual Studio 2010 ярлыком из меню "Пуск", то Visual Studio 2010 запускается без ошибки, и позволяет открыть тот же файл *.sln.

Нужно в свойствах ListBox поменять свойство Anchor (привязка к границам) на Top, Bottom, Left, Right. Для того, чтобы отделить некоторую часть формы от других элементов пользовательского интерфейса, применяйте элемент Panel, на котором размещайте другие элементы интерфейса.

В среде Visual Studio C# удобно делать пакет установки, не нужно разбираться с инсталляторами программы (внешние дополнительные инсталляторы теперь не нужны), которую Вы пишете. Для этого в меню достаточно выбрать в меню Построение (Build) -> Опубликовать (Release). В папке Release сразу появится подпапка app.publish, в которой будет лежать готовый инсталлятор setup.exe, содержащий все необходимые для установки программы компоненты.

Проблема обычно возникает, когда необходимо получить доступ на запись к одному и тому же ресурсу асинхронно из нескольких потоков. Например, Вы имеете на форме ListBox, в которые задумали выводить сообщения из обработчика события, который обрабатывается в другом потоке. У меня такое произошло, когда понадобилось отображение данных в ListBox, принятых в обработчике события поступления данных по USB.

Почему такая проблема возникает: ListBox создается в основном потоке (thread), запускающей GUI окна программы. Другие потоки могут быть созданы для поддержки обмена данными с внешними устройствами (сеть Ethernet, COM-порт, USB и т. п.). Эти другие потоки могут обрабатывать события приема данных асинхронно с выполнением основного потока. Поэтому если эти потоки попытаются что-то записать в ListBox, то это может привести к конфликту с записью в ListBox данных из основного потока GUI интерфейса программы.

Как проблему можно решить: для ListBox создаются подпрограммы (методы), через которые выводятся данные в ListBox. Данные выводятся в ListBox только через эти подпрограммы, больше никак. Эти подпрограммы делаются защищенными (thread-safe) для вызова из любых потоков через delegate, InvokeRequired, Invoke.

См. также:
- справку в MSDN: Control.InvokeRequired - свойство, Control.Invoke - метод (Delegate), How to: Make Thread-Safe Calls to Windows Forms Controls
- Making Windows Forms thread safe

В C# уже нельзя, как раньше, беспечно задавать константы операторами #define, и добавлять их h-файлы (как было принято в языках C и C++). Для C# нельзя использовать заголовки (файлы с расширением *.h или *.hpp), и директива #define не может использоваться для задания констант.

Для использования #define теперь такие правила:

1. Добавлять определения констант #define можно только в начале файла, иначе получите ошибку CS1032.
2. Директива define может использоваться только для задания ключей компиляции, но не для задания констант, иначе получите ошибку CS1025: Требуется однострочный комментарий или признак конца строки.

Константы в C# задаются как обычные переменные, только с помощью ключевого слова const:

const byte USBCAN_SUCCESSFUL = 0x00;

Однако если Вы попытаетесь сделать это вне пространства имен класса, то получите ошибку CS0116: Пространство имен не может непосредственно содержать такие члены, как поля или методы. Константы нужно определять не только внутри скобок одной из секций namespace, но и в каком-то определенном классе. Например так (определение константы USBCAN_SUCCESSFUL):

namespace MyApp
{ public partial class Form1 : Form { USBcanServer CANsrv = new USBcanServer(); byte bRet = 0; //код возврата вызовов методов USBcanServer   const byte USBCAN_SUCCESSFUL = 0x00;   public Form1() { InitializeComponent(); }   private void Form1_Load(object sender, EventArgs e) { applog.write("[START]"); bRet = CANsrv.InitHardware(); if (USBCAN_SUCCESSFUL != bRet) applog.write("InitHardware error"); } }
} 

Вместо #define удобно также использовать перечисления (enum). Вот пример задания масок для бит сигналов:

      public enum canreq
      {
         //биты масок сигналов
         ALSN25   = (1 << 8),
         ALSN50   = (1 << 9),
         ALSN75   = (1 << 10),
         ALSEN    = (1 << 11),
         KRL475   = (1 << 12),
         KRL525   = (1 << 13),
         KRL575   = (1 << 14),
         KRL625   = (1 << 15),
         KRL675   = (1 << 16),
         KRL725   = (1 << 17),
         KRL775   = (1 << 18),
         KRL825   = (1 << 19),
         KRL875   = (1 << 20),
         KRL925   = (1 << 21),
         TRC425   = (1 << 22),
         TRC475   = (1 << 23),
         TRC575   = (1 << 60),
         TRC725   = (1 << 61),
         TRC775   = (1 << 62)
      }

На примере добавления события приема сообщения CAN (класс-обертка USBcanServer над функциями библиотеки UcanDotNET.dll для модулей USB CAN компании SYSTEC):

CANsrv.CanMsgReceivedEvent += new USBcanServer.CanMsgReceivedEventEventHandler(CANsrv_CanMsgReceivedEvent);

И еще добавьте обработчик события приема сообщения CANsrv_CanMsgReceivedEvent:

private void CANsrv_CanMsgReceivedEvent(byte bChannel_p)
{ applog.write("Data arrived");
}

Для упрощенного добавления обработчика выполните следующие шаги:

- в коде Form1_Load напечатайте CANsrv. (в конце поставьте точку);
- нажмите Ctrl+Space, из появившегося списка методов и событий выберите CanMsgReceivedEvent;
- допечатайте пробел, + и =. Появится подсказка в виде текста:
"new USBcanServer.CanMsgReceivedEventEventHandler(CANsrv_CanMsgReceivedEvent); (Нажмите клавишу TAB для вставки)"
- нажмите клавишу TAB, и в коде автоматически будет создана строка:

CANsrv.CanMsgReceivedEvent += new USBcanServer.CanMsgReceivedEventEventHandler(CANsrv_CanMsgReceivedEvent);

После этого останется вручную создать процедуру CANsrv_CanMsgReceivedEvent. Полный код см. в [2].

Пример конвертации структуры tCanMsgStruct с языка C++:

typedef struct _tCanMsgStruct
{ DWORD m_dwID; // CAN Identifier BYTE m_bFF; // CAN Frame format (BIT7=1: 29BitID / BIT6=1: RTR-Frame / BIT5=1: Tx echo) BYTE m_bDLC; // CAN Data Length Code BYTE m_bData[8]; // CAN Data DWORD m_dwTime; // Time in ms
} tCanMsgStruct;

На C# будет так (только для компиляции в небезопасном режиме, с опцией /unsafe):

unsafe public struct tCanMsgStruct
{ UInt32 m_dwID; // CAN Identifier byte m_bFF; // CAN Frame format (BIT7=1: 29BitID / BIT6=1: RTR-Frame / BIT5=1: Tx echo) byte m_bDLC; // CAN Data Length Code fixed byte m_bData[8]; // CAN Data byte m_dwTime; // Time in ms
}

Если код компилируется в безопасном режиме, то на C# структура tCanMsgStruct должна быть определена так:

using System.Runtime.InteropServices;
...
[StructLayout(LayoutKind.Explicit, Pack=1, Size=15)]
public struct tCanMsgStruct
{ [FieldOffset(0)] public UInt32 m_dwID; [FieldOffset(4)] public byte m_bFF; [FieldOffset(5)] public byte m_bDLC; [FieldOffset(6)] public byte m_bData; [FieldOffset(14)] public byte m_dwTime;
}

Экземпляр структуры определяется так:

tCanMsgStruct msg = new tCanMsgStruct;

Если не нужно точное соответствие положения полей в памяти, то структуры можно определять обычным способом, через ключевое слово struct. Такие структуры на C# почти что классы, и могут иметь даже собственные конструкторы, в которых можно инициализировать массивы, входящие в структуру как поле. Как и классы, в составе полей такие структуры могут иметь классы, атрибуты и методы. Пример структуры с конструктором:

public struct tFirmware
{ public int minadr; public int maxadr; public byte[] data; public tFirmware (int dummy) { minadr = 65535; maxadr = 0; data = new byte[65536]; //Заполнение массива данных файла байтами FF. Это соответствует чистому // состоянию памяти FLASH микроконтроллера AVR. for (int idx = 0; idx < data.Length; idx++) data[idx] = 0xFF; }
} 
..
tFirmware firmware = new tFirmware();
//заполнение массива данными прошивки
for (int idx = 0; idx < bytes_in_line; idx++)
{ fw.data[hexadr + idx] = GetHexLineByte(s, idx); //модификация минимального и максимального адреса прошивки if ((hexadr + idx) < fw.minadr) fw.minadr = hexadr + idx; if ((hexadr + idx) > fw.maxadr) fw.maxadr = hexadr + idx;
}

Пример определения экземпляров массивов:

int[] myarray2;
int[] myArray3 = new int[2] {1,2} UcanDotNET.USBcanServer.tCanMsgStruct[] msg = new UcanDotNET.USBcanServer.tCanMsgStruct[10];

Если компилируется небезопасный код (применяется опция /unsafe), то можно определять массивы так:

unsafe fixed byte m_bData[8]; 

[Многомерные массивы]

Определение массива 2x8 байт:

  public byte [,] buf = new byte [2, 8];

UInt32 - беззнаковое целое, 32 разряда, заменяет DWORD. Удобно также использовать определения других беззнаковых целых byte, UInt16, UInt64 (соответственно 8, 16, 64 бита).

int  a = 1;
byte b = 2;
string tmptxt = String.Format("a={0} b={1} ", a, b); 

См. также:

Преобразование чисел, даты, времени в строку: Q014, Q015, Q016, Q019, Q054.

Преобразование строк в числа: Q022.

byte bval = 0xAA;
//Если в формате применить маленькую букву x, то буквенные шестнадцатеричные цифры
// будут выводиться маленькими буквами
string tmptxt = String.Format("bval=0x{0:x2}", bval);
//Если в формате применить большую букву x, то буквенные шестнадцатеричные цифры
// будут выводиться большими буквами
string tmptxt = String.Format("bval=0x{0:X2}", bval); 

См. также:

Преобразование чисел, даты, времени в строку: Q014, Q015, Q016, Q019, Q054.

Преобразование строк в числа: Q022.

Полностью аналогичного функционала static добиться нельзя. На C# невозможно создать внутри метода класса статическую переменную, чтобы она сохраняла свое значение между вызовами метода. Чтобы достичь этой цели, нужно объявить обычную глобальную переменную класса, и она будет видна не только этому методу класса, но и всем методам класса, и будет сохранять свое значение между вызовами метода.

На языке C# значение ключевого слова static поменялось. Это слово можно использовать только для глобальных переменных класса, и оно будет означать следующее - переменная класса, которая объявлена с ключевым словом static, будет иметь общее значение между всеми экземплярами этого класса. Т. е. она будет "глобальной" для всех экземпляров этого класса.

Меню Проект -> Добавить форму Windows... -> Форма Windows Forms -> OK. По умолчанию будет добавлена форма Form2. Далее в модуле Program.cs добавьте код для отображения второй формы:

...
static void Main()
{    Application.EnableVisualStyles();    Application.SetCompatibleTextRenderingDefault(false);
   Form2 frm = new Form2();
   frm.Show();
   Application.Run(new Form1());
} ...

После запуска программы отобразятся сразу две формы, Form1 и Form2. Подробности см. в [4].

Форматированный вывод в строку (то, что на C++ делает printf с параметрами форматирования %) в C# можно делать через метод Format, который есть у класса string. Тип форматирования зависит не только от строки формата, но и от типа выводимых параметров. Строка формата задается теперь не символом %, а фигурными скобками. Первое число, которое идет после открывающей фигурной скобки, задают номер выводимого в строку параметра. Если после этого числа стоит двоеточие, то за ним идут символы до закрывающей фигурной скобки, которые определяют формат вывода параметра. Для того, чтобы понять, как пользоваться Format, проще рассмотреть несколько простых примеров.

////////////////////////////////////////////////////////////////////////////////
// Вывод числа float с нужным количеством знаков после запятой
string strvalue;
float fvalue = 123.456; //123.456 -> "123": strvalue = string.Format("{0:0}", fvalue);
//123.456 -> "123.5": strvalue = string.Format("{0:0}", fvalue);
//123.456 -> "123.46": strvalue = string.Format("{0:00}", fvalue);
//123.456 -> "123.456": strvalue = string.Format("{0:000}", fvalue);
////////////////////////////////////////////////////////////////////////////////
// Вывод строки strvalue = "Hello"; strvalue = string.Format("{0}, World!", strvalue); // -> "Hello, World!"
////////////////////////////////////////////////////////////////////////////////
// Вывод шестнадцатеричного числа UInt16 u16 = 0x12AB;
// -> "12ab":
strvalue = string.Format("{0:x4}", u16);
// -> "12AB":
strvalue = string.Format("{0:X4}", u16);
// -> "0x012AB":
strvalue = string.Format("0x{0:X5}", u16);
// -> "0x12 0xAB":
strvalue = string.Format("0x{0:X2} 0x{1:X2}", (UInt16)(u16>>2), u16);

См. также:

Преобразование чисел, даты, времени в строку: Q014, Q015, Q016, Q019, Q054.

Преобразование строк в числа: Q022.

На языке C# переменное количество параметров передается функции в виде безразмерного массива. На примере класса log - в него добавлен метод write, которому можно передавать в виде параметров любое количество строк:

namespace MyApp
{ class log { public void write(params string[] args) { DateTime currtime = DateTime.Now; string tmptxt;   tmptxt = String.Format("{0:yyMMdd hh:mm:ss} ", currtime); for (int i = 0; i < args.Length; i++) { tmptxt = tmptxt + args[i]; }   try { using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"log.txt", true)) { file.WriteLine(tmptxt); file.Close(); } } catch { } } }
}

Пример использования функции с переменным числом параметров (запись в лог):

int iVal = 1476;
float fPi = 3.14;
byte bVal = 0xAA;   ...
log applog = new log();   ...
applog.write("Hello, World!"); applog.write("iVal=", iVal.ToString()); applog.write("fPi=", iVal.ToString(), " bVal=", bVal.ToString()); ...

Пример:

...
private void Form1_Load(object sender, EventArgs e)
{ this.Location.X = 1; //ошибка CS1612 ...

Ответ: изменять свойство Location нужно через вспомогательную переменную следующим образом:

   ...
   Point tmpLocation = this.Location;
   tmpLocation.X = 1;
   tmpLocation.Y = 1;
   this.Location = tmpLocation;
   ...

Похожим образом нужно менять и размер окна формы, так как прямым присвоением Form.Size.Width и Form.Size.Height этого сделать нельзя (появится ошибка CS1612). Менять размер формы нужно так:

this.Size = new System.Drawing.Size(newWidth, newHeight);

См. методы класса Convert - ToBoolean, ToByte, ToChar, ToDecimal, ToDouble, ToInt16, ToInt32, ToInt64, ToUInt16, ToUInt32, ToUInt64 и другие. Пример преобразования строки в целое число со знаком:

int iVal = Convert.ToInt32("1234");

Пример преобразования строки символов в HEX-формате:

string tmptxt = "0x89AB";
UInt16 val16 = (UInt16)Convert.ToInt16(tmptxt, 16);

См. также:

Преобразование чисел, даты, времени в строку: Q014, Q015, Q016, Q019, Q054.

Преобразование строк в числа: Q022.

На примере сохранения положения и размеров окна при выходе из программы и восстановления параметров окна при запуске программы:

private void Form1_Load(object sender, EventArgs e){
   //чтение координат и размеров окна
   Microsoft.Win32.RegistryKey key;
   key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey("MyApp-subkey");
   if (null != key.GetValue("Form1-X"))
      {
         Point tmpLocation = this.Location;
         tmpLocation.X = Convert.ToInt32(key.GetValue("Form1-X").ToString());
         tmpLocation.Y = Convert.ToInt32(key.GetValue("Form1-Y").ToString());
         this.Location = tmpLocation;
         this.Size = new System.Drawing.Size(Convert.ToInt32(key.GetValue("Form1-Width").ToString()),
                                             Convert.ToInt32(key.GetValue("Form1-Height").ToString()));
      }
      key.Close();
      ...
   private void Form1_FormClosed (object sender, FormClosedEventArgs e)
   {
      //запись координат и размеров окна
      Microsoft.Win32.RegistryKey key;
      key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey("MyApp-subkey");
      key.SetValue("Form1-X", Location.X);
      key.SetValue("Form1-Y", Location.Y);
      key.SetValue("Form1-Width", Size.Width);
      key.SetValue("Form1-Height", Size.Height);
      key.Close();
      ...

По умолчанию, когда Вы создаете ComboBox (путем перетаскивания его на форму с Панели элементов), свойство DropDownStyle установлено в значение DropDown. Совершенно неочевидно, что DropDown соответствует разрешенному редактированию текущего элемента. Чтобы запретить редактирование, измените свойство DropDownStyle на DropDownList.

Чтобы при запуске программы в ComboBox отображалось нужное значение из списка Items, программно поменяйте свойство SelectedIndex.

Подробности по ComboBox см. в [3].

Очистка структуры делается с помощью ключевого слова default (тут надо указать тип очищаемой структуры). Пример:

      [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 8)]
      public struct TAlsenStr
      {
         [FieldOffset(0)]
         public UInt16 CentralFrequency;  // Центральная частота, десятые доли герца
         [FieldOffset(2)]
         public UInt32 ValueRMS;          // СКЗ сигнала, сотые доли мВ
         [FieldOffset(6)]
         public byte CodeChannel1;        // Номер кодовой комбинации первого подканала
         [FieldOffset(7)]
         public byte CodeChannel2;        // Номер кодовой комбинации второго подканала
      }
 
      TAlsenStr ALSEN;
      //работа со структурой АЛСЕН, присвоение значений полям:
      ALSEN.CentralFrequency = 1745;
      ALSEN.ValueRMS = 3000;
      ...
      //очистка структуры, всем полям будут присвоены нули:
      ALSEN = default(TAlsenStr);

Несмотря на то, что ошибка расшифрована в MSDN, там не приводится метод её решения, и смысл ошибки на первый взгляд может быть неочевиден. Возможная причина ошибки - при объявлении класса [namespace.]класс1/тип пропущено ключевое слово public. Например, если дает ошибку следующее объявление класса:

      public MyClass classVar = new MyClass();   //тут выскакивает ошибка CS0052
namespace MyApp
{ class MyClass { //далее определение класса ...

то попробуйте исправить определение класса MyClass, добавив public:

namespace MyApp
{ public class MyClass { //далее определение класса ... 

На языке C# модификатор static используется для объявления статического члена, принадлежащего собственно типу, а не конкретному объекту. Применение static к переменной класса означает, что эта переменная сохраняет свое значение для всех экземпляров этого класса. То есть эта переменная как бы общая для всех экземпляров этого класса. Например, если мы определим класс MyClass, и внутри него определим переменную static int cnt, то эту переменную можно использовать для подсчета созданных экземпляров класса MyClass, потому что переменная cnt будет иметь одно и то же значение для всех созданных экземпляров MyClass. Для C/C++ static имеет другое значение, потому что предназначено для сохранения значения переменной между вызовами функции, и для ограничения видимости переменной в пределах модуля.

Модификатор static можно использовать с классами, полями, методами, свойствами, операторами, событиями и конструкторами, но нельзя — с индексаторами, деструкторами или типами, отличными от классов. На статический член нельзя ссылаться через экземпляр, а можно только через имя типа. Подробности смотрите в MSDN, раздел "static (Справочник по C#)".

Такое может произойти, если рекурсивно закрывается одна форма из другой, в результате закрытие 1-й формы вызовет обработчик FormClosed 1-й формы, который вызовет обработчик FormClosed 2-й формы, который в свою очередь вызовет обработчик FormClosed 1-й формы, и так далее по циклу, пока стек приложения не переполнится. Например, Ваше приложение имеет несколько форм - Form1, Form2, Form3, и нужно, чтобы закрытие одной формы привело к закрытию всего приложения. Если Вы напишете код, который будет закрывать все формы подряд в каждом обработчике FormClosed, то это приведет к вышеуказанной ошибке. Вот пример ошибочного кода (Form1 главная форма, которая запускается методом Application.Run):

      private void Form1_FormClosed(object sender, FormClosedEventArgs e)
      {
         //тут действия, выполняемые при закрытии приложения
         ...
      }
 
      private void Form2_FormClosed(object sender, FormClosedEventArgs e)
      {
         Program.Form3.Close();  //тут произойдет ошибка System.StackOverflowException
         Program.Form1.Close();
      }
 
      private void Form3_FormClosed(object sender, FormClosedEventArgs e)
      {
         Program.Form2.Close();  //тут произойдет ошибка System.StackOverflowException
         Program.Form1.Close();
      }

А вот так нужно исправить код, чтобы не возникала ошибка System.StackOverflowException в System.Windows.Forms.dll:

      private void Form1_FormClosed(object sender, FormClosedEventArgs e)
      {
         //тут действия, выполняемые при закрытии приложения
         ...
      }
 
      private void Form2_FormClosed(object sender, FormClosedEventArgs e)
      { //закрытие Form1 завершит программу, т. е. будут автоматически закрыты Form1, и Form3
         Program.Form1.Close();
      }
 
      private void Form3_FormClosed(object sender, FormClosedEventArgs e)
      {  //закрытие Form1 завершит программу, т. е. будут автоматически закрыты Form1, и Form2
         Program.Form1.Close();
      } 

Используйте метод Clear. Пример:

namespace MyApp
{ public partial class Form1 : Form { public Form1() { frmlog = new log(this.GetType().ToString()); InitializeComponent(); }   public void Clean() { System.Drawing.Graphics formGraphics = this.CreateGraphics(); //серый цвет, составленный из компонентов R, G, B Color formcolor = Color.FromArgb(235, 234, 219);   formGraphics.Clear(formcolor); }   .. }
} .. Clean(); //очистка поверхности формы

В выражении частное и делитель имеют разные, несовместимые типы. Попробуйте и частное, и делитель привести к типу float, для этого добавьте префикс (float), который обеспечит явное приведение типа переменной.

float var1, result;
int var2;  ...
result = var1 / var2;             //ошибка CS0019
result = var1 / (float)var2;      //ОК

Нужно во время разработки задать у формы свойства MinimumSize и MaximumSize на нужные значения, и сделайте их одинаковыми. Также задайте свойство формы MaximizeBox в значение false. После этого пользователь, запустив программу, не сможет поменять размер окна, и не сможет развернуть окно на весь экран.

Второй способ зафиксировать размер формы во время выполнения - задайте обработчик события OnResize, в котором установите свойства Width и Height в нужное значение.

      private void Form1_Resize(object sender, EventArgs e)
      {
         Width  = 640;
         Height = 480;
      }

Используйте метод MeasureString класса System.Drawing.Graphics. Пример:

      public float GetTextWidthInPixels(string measuredstring)
      {
         const string FontName = "Lucida Console";
         const int FontSize = 8;
 
         SizeF stringSize = new SizeF();
         Font stringFont = new Font(FontName, FontSize);
         int stringWidth = 2000; // Установка максимальной ширины строки.
         System.Drawing.Graphics formGraphics = this.CreateGraphics();
         stringSize = formGraphics.MeasureString(measuredstring, stringFont, stringWidth);
         return stringSize.Width;
      }

Этим же способом можно узнать и высоту текста в пикселах (для нашего примера это будет stringSize.Height).

Решить проблему можно, если отключить для отладки возможность IntelliTrace. Делается это следующим образом - меню Сервис -> пункт Параметры -> разверните узел IntelliTrace -> щелкните Общие -> снимите флажок Включить IntelliTrace -> нажмите кнопку ОК.

Используйте метод ShowDialog(), подробности см. в [4].

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

c-sharp-timer-component

Однако можно создать экземпляр класса (компонент) во время выполнения программы, что решит проблему. На примере таймера Timer с Панели Элементов, который добавляется программно в невизуальный класс MyClass:

using System;
using System.Collections.Generic;
using System.Linq;using System.Text;
namespace MyApp
{ class MyClass { ... //переменная - хранилище для экземпляра класса таймера private System.Windows.Forms.Timer mytimer; //счетчик времени в единицах 100 мс public UInt32 time100stamp = 0;   //////////////////////////////////////////////////////////////// /// Конструктор класса MyClass public MyClass() { ... //создание экземпляра класса таймера во время выполнения mytimer = new System.Windows.Forms.Timer(); //интервал таймера 100 мс mytimer.Interval = 100; //добавление события на срабатывание таймера mytimer.Tick += new System.EventHandler(this.mytimer_Tick); //запуск таймера mytimer.Start(); ... }   //////////////////////////////////////////////////////////////// /// Событие 100-мс таймера, которое просто инкрементирует /// счетчик времени. private void mytimer_Tick(object sender, EventArgs e) { time100stamp++; }   ... }
}

Используйте this.GetType().ToString(), получите что-то типа "myApp.Form1".

См. также Q053.

Предположим, что наш проект называется myApp. В таблице перечислены методы выполнения этих действий.

Методы

Пояснения

System.AppDomain.CurrentDomain.FriendlyName

Имя исполняемого файла в форме myApp.vhost.exe

System.AppDomain.CurrentDomain.BaseDirectory

Каталог, где находится исполняемый файл (полный путь каталога).

System.Reflection.Assembly.GetExecutingAssembly().FullName

Получим строку типа "myApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

System.Reflection.Assembly.GetExecutingAssembly().Location

Полный путь до исполняемого файла в виде c:\VisualStudioProjects\myApp\bin\Debug\myApp.exe

Path.GetFileNameWithoutExtension(fullname)

Получение имени исполняемого файла без пути и расширения, получим что-то типа строки "myApp".

Примеры:

//изменение расширения файлаusing System.IO;
string logfilename;
logfilename = System.Reflection.Assembly.GetExecutingAssembly().Location; logfilename = Path.ChangeExtension(logfilename, ".log");
//Как получить имя исполняемого файла без расширения?
using System.IO;

string
fullname = System.Reflection.Assembly.GetExecutingAssembly().Location;
string name_without_ext = Path.GetFileNameWithoutExtension(fullname);

Если цикл длинный, то программа начинает заметно тормозить как саму себя (интерфейс программы работает плохим откликом), так и компьютер. Если Вы запустили отдельный поток для каких-нибудь действий (например, ждете данные от сокета, или последовательного порта), то цикл без задержки нежелателен, нужно отдавать ресурсы процессора другим потокам и программам. Делается такая операция обычно с помощью Sleep(ms). Пример введения задержки (например, для приостановки цикла) на 10 миллисекунд:

System.Threading.Thread.Sleep(10);

Пример таких ошибок:

Ошибка 17 Не удалось опубликовать из-за того, что не удалось построить проект. 1
Ошибка 18 При подписи произошла ошибка: Не удалось подписать bin\Release\app.publish\\setup.exe. SignTool Error: The signer's certificate is not valid for signing.
SignTool Error: An error occurred while attempting to sign: bin\Release\app.publish\\setup.exe C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets 3987

Решение проблемы: зайдите в меню Проект -> Свойства проекта, снимите галочку "Подписать манифесты ClickOnce". После запуска публикации система выдаст запрос: "Приложение подписано не тем ключом, что существующее на сервере приложение. Перезаписать его?", ответьте утвердительно.

Известно, что объединение (union) на языках C и C++ позволяет интерпретировать одни и те же данные по-разному. Например, 32-разрядные данные (int) можно представить как два 16-разрядных слова (short) или как 4 отдельные байта (unsigned char).

typedef union _CANdata
{ u32 d32 [2]; u16 d16 [4]; u8 d8 [8];
}TCANdata;

К сожалению, C# не поддерживает объединения C/C++. Однако с той же целью можно использовать StructLayout(LayoutKind.Explicit) и атрибуты FieldOffset. Предположим, что у Вас есть структура из 64 бит данных, и Вам нужно представить её либо как 8 байт (byte), либо как два целых 32-разрядных числа (int). Атрибут FieldOffset регулирует смещение нужных нам частей данных. Например:

using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
struct MyArray
{ //здесь начинаются 8 байт данных [FieldOffset(0)] public byte Byte1; [FieldOffset(1)] public byte Byte2; [FieldOffset(2)] public byte Byte3; [FieldOffset(3)] public byte Byte4; [FieldOffset(4)] public byte Byte5; [FieldOffset(5)] public byte Byte6; [FieldOffset(6)] public byte Byte7; [FieldOffset(7)] public byte Byte8; //здесь те же 8 байт интерпретируются как два 32-битные числа [FieldOffset(0)] public int Int1; [FieldOffset(4)] public int Int2;
}

[Пример1: проблема с ulong и int]

При попытке задать в перечислении 64-битную величину получаю ошибку CS0266 "Не удается неявно преобразовать тип "ulong" в "int". Существует явное преобразование (возможно, пропущено приведение типов) C:\csharpprj\protocol.cs ...".

      public enum request_bits
      {
         chnmask = 0x3F,
         //биты масок сигналов
         ARS75 = (1 << 8),
         ARS125 = (1 << 9),
         ARS175 = (1 << 10),
         ARS225 = (1 << 11),
         ARS275 = (1 << 12),
         ARS325 = (1 << 13),
         reserved = (0x3FFFFFFFFFFFF << 14)  //ошибка CS0266
      }

Решение проблемы: по умолчанию перечисление (enum) на C# подразумевает использование целых чисел (int). Чтобы исправить ошибку, добавьте явное указание типа для перечисления (можно указать типы byte, sbyte, short, ushort, int, uint, long или ulong):

      public enum request_bits : ulong
      {
         chnmask = 0x3F,
         //биты масок сигналов
         ARS75 = (1 << 8),
         ARS125 = (1 << 9),
         ARS175 = (1 << 10),
         ARS225 = (1 << 11),
         ARS275 = (1 << 12),
         ARS325 = (1 << 13),
         reserved = ((ulong)0x3FFFFFFFFFFFF << 14)
      }

[Пример2: проблема с типом byte]

Вот такой простой код неожиданно дает ошибку CS0266:

byte b1, b2;
 
b1 = 0;
b2 = b1+1;    //ошибка CS0266...

Проблема тут в том, что оператор + в дает в результате тип int. Решение проблемы:

byte b1, b2;
 
b1 = 0;
b2 = (byte)(b1+1);

Для сравнения используйте метод String.Compare. Он возвращает целое число, которое показывает их относительное положение в порядке сортировки. Результат равен нулю, если сравниваемые строки одинаковы. Простые примеры:

if (0 == String.Compare(strA, strB, true))
{ //строки strA и strB совпадают без учета регистра ..
}if (0 == String.Compare(strA, strB))
{ //Сюда попали, если строки равны по критерию // сортировки. Внимание: на сравнение при таком // вызове влияют правила учета регистра, связанные // с языком и региональными параметрами. //Поэтому этот вариант перегрузки String.Compare // использовать нежелательно. ..
}

(Даниил Захаров): Деструктор в С# несколько отличается от деструктора в С++.

В С++ он вызывается напрямую механизмами языка. Это происходит так же быстро, как и вызов метода из кода пользователя (ну, пожалуй, совсем ненамного дольше).

В С# деструкторы вызываются не пользовательским кодом, а средой CLR. Код C# потому и называется управляемым, что по сути он запускается под управлением среды. Поэтому, к примеру, здесь нет указателей - среда сама выполняет операции с указателями автоматически и не поддерживает код, который их использует. Механизм использования указателей все же оставлен в целях совместимости, но при этом классы, их использующие, надо помечать ключевым словом unsafe, разрешать выполнение этого unsafe кода в свойствах проекта. Но самое главное - среда не будет управлять этим кодом - она передаст ему управление и будет дожидаться, когда получит его обратно.

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

Моя практика показала, что при уничтожении около полусотни объектов с деструкторами (причем до крайности простыми - из статической переменной вычиталась единица) комп "подвисает" так, что это становится заметно пользователю. Замеры показали диапазоны 18 .. 47 мс, т. е. почти всегда "терялся" один кадр на экране, а в некоторых случаях - до двух/трех (при частоте смены кадров в 30 Гц).

См. также Q052.

Использование директивы using объявляет пространство поиска используемых имен в библиотеках C#, что позволяет упростить и сократить текст программы. Для примера - использование using System.IO позволяет обойтись без постоянного упоминания префикса System.IO:

//Без using System.IO:
..
if (!System.IO.File.Exists("readme.txt"))
{ //..
}
//С использованием using System.IO:
using System.IO; ..
if (!File.Exists("readme.txt"))
{ //..
}

В консольном и GUI-приложении можно создать и отобразить окно с помощью класса MessageBox. В этом окне можно отобразить текст, кнопки и символы, которые информируют пользователя о чем-то и/или дают ему указания. При этом работа приложения блокируется до тех пор, пока пользователь не закроет окно (стандартная особенность модального окна). Более подробно об использовании MessageBox, о его свойствах и методах см. справку MSDN (запрос для Google MessageBox site:msdn.microsoft.com). Простейший пример вызова MessageBox:

using System.Windows.Forms;
 
..
MessageBox.Show("Hello World!");

MessageBoxHelloWorld-C-sharp

Если при попытке компиляции выводится ошибка о том, что System.Windows.Forms не найдено в пространстве имен ('Forms' does not exist in the namespace system.windows), то добавьте на него ссылку в проекте. Для этого сделайте правый клик на дерево файлов проекта (Solution Tree), выберите в контекстном меню Добавить ссылку..., в появившемся окне "Добавить ссылку" перейдите на закладку .NET, прокрутите список и выберите в нем System.Windows.Forms, нажмите OK.

Пример:

При подписи произошла ошибка: Не удалось подписать binReleaseapp.publishsetup.exe.
SignTool Error: The signer's certificate is not valid for signing.
SignTool Error: An error occurred while attempting to sign: binReleaseapp.publishsetup.exe имя_проекта

Как исправить: зайдите в свойства проекта (Project -> Properties...), перейдите в раздел Подписывание (Signing), нажмите кнопку "Создать тестовый сертификат..." (create test certificate). Появится окно запроса пароля сертификата. Оставьте поля пустыми и нажмите OK, после чего опубликуйте проект.

Процесс по шагам:

1. Откройте Панель элементов, выберите PictureBox, бросьте на форму.
2. Выберите свойства pictureBox1 -> BackgroundImage, нажмите справа кнопку с многоточием, откроется окно выбора ресурса. Выберите уже имеющийся ресурс, или нажмите внизу кнопку Импорт..., и выберите нужную картинку (формата BMP или PNG).
3. По умолчанию свойство BackgroungImageLayout установлено в Tile, что означает, что картинка будет размножена мозаикой по всей площади pictureBox1 (границы этой площади можно раздвинуть мышкой). Чтобы картинка была только одна, выберите значение None для свойства BackgroungImageLayout. Также свойство SizeMode установите в AutoSize, если хотите, чтобы размеры pictureBox1 автоматически подстраивались под размер картинки.
4. Чтобы поменять картинку runtime (во время выполнения программы), нужно присвоить свойство BackgroundImage, пример:

if (!opened)
{
    picDoor1.BackgroundImage = WindowsFormsApplication1.Properties.Resources.door_closed;
}
else { picDoor1.BackgroundImage = WindowsFormsApplication1.Properties.Resources.door_open; }

Здесь door_closed и door_disabled - ссылки на импортированные картинки door_closed.png и door_open.png.

door-closed door-open

Пример:

byte pina, mask;
pina = dev.dev.ReadByte(ATMegaAddresses.PINA); mask = (1 << ATMegaBits.PB0);
// error CS0029: Cannot implicitly convert type 'int' to 'byte'
// (неявное преобразование типа "int" в "bool" невозможно):
if (pina & mask) { ... }

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

//А так нормально, без ошибки:
if (0!=(byte)(pina & mask)) { ... }
//так тоже можно:
if (0 != (pina & mask)) { ... }

Программное рисование производится с помощью методов класса System.Drawing.Graphics. Вот пример, как надо рисовать закрашенный круг:

/// < summary>
/// Рисует над картинкой picture кружок радиусом radius.
/// < /summary>
/// < param name="picture">< /param>
/// < param name="radius">< /param>
private void ShowKnock(PictureBox picture, int radius) { Point center = new Point(picture.Left + (picture.Width / 2), picture.Top - 25); System.Drawing.Graphics graphics = this.CreateGraphics(); System.Drawing.Rectangle rectangle; SolidBrush Brush; //Красный кружок Brush = new SolidBrush(System.Drawing.Color.Red); rectangle = new System.Drawing.Rectangle(center.X - radius, center.Y - radius, radius * 2, radius * 2); graphics.FillEllipse(Brush, rectangle); }

Как нарисовать черную линию:

System.Drawing.Pen myPen;
myPen = new System.Drawing.Pen(System.Drawing.Color.Black);
System.Drawing.Graphics formGraphics = this.CreateGraphics();
formGraphics.DrawLine(myPen, 0, 0, 200, 200);
myPen.Dispose();
formGraphics.Dispose();

Пример рисования незаштрихованных эллипса и прямоугольника:

private void DrawIt()
{
    System.Drawing.Graphics graphics = this.CreateGraphics();
    System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(
       50, 50, 150, 150);
    graphics.DrawEllipse(System.Drawing.Pens.Black, rectangle);
    graphics.DrawRectangle(System.Drawing.Pens.Red, rectangle);
}

В VB такая операция происходит присвоением object=nothing, на C++ применяется оператор delete. Как явно удалять объекты на C#?

В C# можно сделать аналогичное действие object=null, но в действительности присвоение object=null не удаляет объект, а просто делает недействительной ссылку на него. В .NET это дает сигнал сборщику мусора (garbage collector) удалить объект из памяти. Но Вы не можете знать, когда конкретно это произойдет.

В C# при создании объекта оператором new память для него выделяется из кучи (heap). Вы также можете Объект удалится автоматически тем же сборщиком мусора, когда управление выйдет за пределы существование объекта (например, когда завершается функция, которая создала объект оператором new). Но опять-таки неизвестно, в какой момент произойдет освобождение памяти, связанное с удалением объекта.

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

using (Graphics gr = Graphics.FromBitmap(bm))
{
   // тут какие-то действия
   ...
}// здесь будет автоматически вызван gr.Dispose()

Принудительно запустить процедуру сбора мусора можно вызовом GC.Collect(), но это делать не рекомендуется.

См. также Q045.

Для произвольного класса:

Type myType = typeof(MyClass);
// Получение строки namespace класса myClass. Console.WriteLine("Namespace: {0}.", myType.Namespace);

Для строки:

Type myType = typeof(MyClass);
var n = myType.Namespace;

Для метки WinForm:

Type myType = typeof(MyClass);
namespaceLabel.Text = myType.Namespace;

Если у Вас есть экземпляр x класса A внутри namespace B, то можете использовать:

string s = x.GetType().Namespace;

В строке s не будет содержаться "B". Вы можете также использовать x.GetType().Name, чтобы получить имя типа, или x.GetType().FullName чтобы получить полное имя, вместе с B.

Другой вариант, поместите в Вашу сборку функцию:

public static string GetCurrentNamespace()
{
    return System.Reflection.Assembly.GetExecutingAssembly().EntryPoint.DeclaringType.Namespace;
}

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

[System.Runtime.CompilerServices.MethodImpl(MethodImplOptions.NoInlining)]
public static string GetCurrentNamespace() { return System.Reflection.Assembly.GetCallingAssembly().EntryPoint.DeclaringType.Namespace; }

См. также Q038.

[Вариант 1]

Чтобы в Windows Forms нарисовать метку с вертикальным текстом, нужно переопределить событие Paint, и рисовать в нем метку вертикально. Имейте в виду, что GDI оптимизировано для горизонтального рисования меток. Если Вы повернете текст (даже если угол поворота будет кратен 90 градусов), то возможно это будет работать не очень качественно.

Возможно лучшим решением будет нарисовать текст (или поручить метке нарисовать себя) в растровую картинку (bitmap), и затем отобразить эту картинку повернутой.

Ниже приведен код, который рисует Custom Control с вертикальным текстом. Имейте в виду, что текст ClearType не работает, если текст не горизонтальный.

using System.Drawing.Drawing2D;
using System.Windows.Forms;

public partial class VerticalLabel : UserControl { public VerticalLabel() { InitializeComponent(); }
private void VerticalLabel_SizeChanged(object sender, EventArgs e) { GenerateTexture(); }
private void GenerateTexture() { StringFormat format = new StringFormat(); format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; format.Trimming = StringTrimming.EllipsisCharacter;
Bitmap img = new Bitmap(this.Height, this.Width); Graphics G = Graphics.FromImage(img);
G.Clear(this.BackColor);
SolidBrush brush_text = new SolidBrush(this.ForeColor); G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit; G.DrawString(this.Name, this.Font, brush_text, new Rectangle(0, 0, img.Width, img.Height), format); brush_text.Dispose();
img.RotateFlip(RotateFlipType.Rotate270FlipNone);
this.BackgroundImage = img; } }

Можно повернуть текст в событии OnPaint, или в методе Paint:

private void uc1_Paint(object sender, PaintEventArgs e)
{
    string Name;
    var g = e.Graphics;
    g.DrawString(Name, new Font("Tahoma", 8), Brushes.Black, 0, 0,
    new StringFormat(StringFormatFlags.DirectionVertical));
}

[Вариант 2]

Создайте свой класс myLabel, который может поворачивать текст. Этот класс можно будет перетаскивать в форму приложения прямо из панели компонентов ToolBox.

using System.Drawing;

class myLabel:System.Windows.Forms.Label { public int RotateAngle { get; set; } // чтобы повернуть текст public string NewText { get; set; } // чтобы нарисовать текст protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { Brush b =new SolidBrush(this.ForeColor); e.Graphics.TranslateTransform(this.Width / 2, this.Height / 2); e.Graphics.RotateTransform(this.RotateAngle); e.Graphics.DrawString(this.NewText, this.Font,b , 0f, 0f); base.OnPaint(e); } }

Для этого класса можно установить свойства:

mylbl.Text = "";              // текст, который можно поменять свойством NewText
mylbl.NewText = "Hello";
mylbl.AutoSize = false;       // подстройка размера под текст
mylbl.ForeColor = Color.Red;  // цвет текста
mylbl.RotateAngle = -90;      // угол поворота

Улучшенная реализация, поддерживающая автоматическое изменение размеров виджета:

public class RotatingLabel : System.Windows.Forms.Label
{
   private int m_RotateAngle = 0;
   private string m_NewText = string.Empty;
public int RotateAngle { get { return m_RotateAngle; } set { m_RotateAngle = value; Invalidate(); } } public string NewText { get { return m_NewText; } set { m_NewText = value; Invalidate(); } }
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { Func< double, double > DegToRad = (angle) => Math.PI * angle / 180.0; Brush b = new SolidBrush(this.ForeColor); SizeF size = e.Graphics.MeasureString(this.NewText, this.Font, this.Parent.Width); int normalAngle = ((RotateAngle % 360) + 360) % 360; double normaleRads = DegToRad(normalAngle); int hSinTheta = (int)Math.Ceiling((size.Height * Math.Sin(normaleRads))); int wCosTheta = (int)Math.Ceiling((size.Width * Math.Cos(normaleRads))); int wSinTheta = (int)Math.Ceiling((size.Width * Math.Sin(normaleRads))); int hCosTheta = (int)Math.Ceiling((size.Height * Math.Cos(normaleRads))); int rotatedWidth = Math.Abs(hSinTheta) + Math.Abs(wCosTheta); int rotatedHeight = Math.Abs(wSinTheta) + Math.Abs(hCosTheta); this.Width = rotatedWidth; this.Height = rotatedHeight; int numQuadrants = (normalAngle >= 0 && normalAngle < 90) ? 1 : (normalAngle >= 90 && normalAngle < 180) ? 2 : (normalAngle >= 180 && normalAngle < 270) ? 3 : (normalAngle >= 270 && normalAngle < 360) ? 4 : 0; int horizShift = 0; int vertShift = 0; if (numQuadrants == 1) { horizShift = Math.Abs(hSinTheta); } else if (numQuadrants == 2) { horizShift = rotatedWidth; vertShift = Math.Abs(hCosTheta); } else if (numQuadrants == 3) { horizShift = Math.Abs(wCosTheta); vertShift = rotatedHeight; } else if (numQuadrants == 4) { vertShift = Math.Abs(wSinTheta); } e.Graphics.TranslateTransform(horizShift, vertShift); e.Graphics.RotateTransform(this.RotateAngle); e.Graphics.DrawString(this.NewText, this.Font, b, 0f, 0f); base.OnPaint(e); } }

Отличный, рабочий пример есть в MSDN (ключевая строка для поиска ICustomFormatter.Format Method site:msdn.microsoft.com).

binaryformatter.cs

using System;
using System.Globalization;
//using System.Numerics;
 
public class BinaryFormatter : IFormatProvider, ICustomFormatter
{
   // Реализация IFormatProvider.GetFormat
   publicobjectGetFormat(Type formatType)
   {
      // Определение, какой пользовательский объект форматирования запрашивается.
      if (formatType == typeof(ICustomFormatter))
         return this;
      else
         return null;
   }
 
   // Форматирование числа в двоичном виде (binary, B), восьмеричном (octal, O)
   // или шестнадцатеричном (hexadecimal, H).
   public string Format(string format, object arg, IFormatProvider formatProvider)
   {
      // Обработка строки формата.
      int baseNumber;
      
      // Обработка null или пустой строки формата, строки с указателем точности
      // (precision specifier).
      string thisFmt = String.Empty;
      // Распаковка первого символа в строек формата (precision specifier
      // не поддерживается).
      if (! String.IsNullOrEmpty(format))
         thisFmt = format.Length > 1 ? format.Substring(0, 1) : format;
 
      // Получение байтового массива, представляющего числовое значение.
      byte[] bytes;
 
      if (arg is sbyte)
      {
         string byteString = ((sbyte) arg).ToString("X2");
         bytes = newbyte[1] { Byte.Parse(byteString, System.Globalization.NumberStyles.HexNumber ) };
      }
      else if (arg is byte)
      {
         bytes = newbyte[1] { (byte) arg };
      }
      else if (arg is short)
      {
         bytes = BitConverter.GetBytes((short) arg);
      }
      else if (arg is int)
      {
         bytes = BitConverter.GetBytes((int) arg);
      }
      else if (arg is long)
      {
         bytes = BitConverter.GetBytes((long) arg);
      }
      else if (arg is ushort)
      {
         bytes = BitConverter.GetBytes((ushort) arg);
      }
      else if (arg is uint)
      {
         bytes = BitConverter.GetBytes((uint) arg);
      }
      else if (arg is ulong)
      {
         bytes = BitConverter.GetBytes((ulong) arg);
      }
      //else if (arg is BigInteger)
      //{
      // bytes = ((BigInteger) arg).ToByteArray();
      //}
      else
      {
         try
         {
            return HandleOtherFormats(format, arg);
         }
         catch (FormatException e)
         {
            throw new FormatException(String.Format("The format of '{0}' is invalid.", format), e);
         }
      }
  
      switch (thisFmt.ToUpper())
      {
      case"B":
         baseNumber = 2;
         break;
      case"O":
         baseNumber = 8;
         break;
      case"H":
         baseNumber = 16;
         break;
      // Обработка не поддерживаемых строк формата.
      default:
         try
         {
            return HandleOtherFormats(format, arg);
         }
         catch (FormatException e)
         {
            throw new FormatException(String.Format("The format of '{0}' is invalid.", format), e);
         }
      }
 
      // Возврат отформатированных строк.
      string numericString = String.Empty;
 
      for (int ctr = bytes.GetUpperBound(0); ctr >= bytes.GetLowerBound(0); ctr--)
      {
         string byteString = Convert.ToString(bytes[ctr], baseNumber);
         if (baseNumber == 2)
            byteString = new String('0', 8 - byteString.Length) + byteString;
         else if (baseNumber == 8)
            byteString = new String('0', 4 - byteString.Length) + byteString;
         else  // База 16.
            byteString = new String('0', 2 - byteString.Length) + byteString;
         numericString += byteString + " ";
      }
      return numericString.Trim();
   }
 
   private string HandleOtherFormats(string format, object arg)
   {
   if (arg is IFormattable)
      return ((IFormattable)arg).ToString(format, CultureInfo.CurrentCulture);
   else if (arg != null)
      return arg.ToString();
   else
      return String.Empty;
   }
}

Пример использования:

label1.Text = String.Format(new BinaryFormatter(), "{0:B}", value);

См. также:

Преобразование чисел, даты, времени в строку: Q014, Q015, Q016, Q019, Q054.

Преобразование строк в числа: Q022.

'Необработанное исключение типа "System.BadImageFormatException" произошло в имя_приложения.exe

Дополнительные сведения: Была сделана попытка загрузить программу, имеющую неверный формат. (Исключение из HRESULT: 0xXXXXXXXX)'

System.BadImageFormatException

Чаще всего проблема из-за того, что вызов функции (метода) потребовал вызова DLL, скомпилированной под другую систему. Например, программа работает на 64-битной версии Windows, а DLL сделана для 32-битной версии. Что можно сделать:

1. Найти DLL, которая скомпилирована под Вашу операционную систему.

2. Если у Вас 64-битная система, а загружаемая DLL 32-битная, то можно изменить в свойствах тип конечной платформы на 32-битную. Для этого откройте свойства проекта, перейдите в раздел Построение, и выберите в выпадающем списке "Конечная платформа:" вариант x86 (см. скриншот ниже). После этого пересоберите проект.

Csharp change platform

Для этого достаточно поменять свойство формы Text. Пример:

[Текст модуля myform.cs]

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
 
namespace myprog
{
   /// < summary >
   /// Главная форма/окно программы.
   /// < /summary >
   public partial class myform : Form
   {
      ...
      private void UpdateForm(bool force_update)
      {
         //рисование заголовка формы
         Text = "Это будет новый текст на плашке формы";
         ...
      }
   ...

Можно также к свойству Text обращаться через объект this:

         this.Text = "Это будет новый текст на плашке формы";

Для этого создайте событие FormClosing, и в его обработчике присвойте FormClosingEventArgs.Cancel значение true. Пример:

      private void myform_FormClosing(object sender, FormClosingEventArgs e)
      {
         e.Cancel = true;
      }

После этого на форме будет отобразаться стандартная кнопка с крестиком, но она перестанет действовать. Подробнее про поведение события закрытия формы см. [6].

Можно ли в C# декларировать переменную таким образом, чтобы в каждом классе (представленном модулем *.cs) можно было получить доступ к содержимому этой переменной без необходимости создавать экземпляр какого-либо класса? Другими словами, если ли в C# глобальные переменные, не привязанные к какому-либо экземпляру класса? Есть ли какая-то альтернатива обмениваться данными между классами через одну внешнюю переменную?

В C# не существует такого понятия, как глобальная переменная, и точка. Однако есть некий аналог в виде модификатора public static, который можно применять к классам и к отдельным членам класса. Коточе говоря, используйте класс типа public static, и получайте доступ к нему где угодно и откуда угодно.

public static class MyStaticValues
{
   public static bool MyStaticBool {get;set;}
}
 
public static class Globals
{
    public static String s_Name = "Mike";    // Эту переменную можно модифицировать в коде
    public const Int32 VALUE = 10;           // Эту нельзя
}

Можно использовать переменные этого класса в любом месте, когда они находятся в одном и том же пространстве имен (namespace), что и ссылающийся на переменные код. Можно объявить MyStaticValues или Globals без пространства имен (что поместит эти статические классы в глобальное пространство имен приложения, application namespace), или Вы работаете с разными пространствами имен, то можете указать нужное пространство следующей директивой:

string name = Globals.s_Name;

[Как использовать аналоги глобальных переменных в C#]

Сначала хорошенько подумайте - действительно ли Вам нужна глобальная переменная. Скорее всего решение должно быть принято, исходя из рассмотрения техзадания на разрабатываемое программное обеспечение. Обычно статические переменные могут пригодиться для таких объектов в программе, которые имеются исключительно в одном экземпляре, их не нужно создавать и удалять динамически, и если эти объекты подразумеваются для использования несколькими разными классами в программе. Имейте в виду, что иногда проблемы, возникающие из-за конкуренции доступа к глобальным переменным (особенно актуально в сложных системах), бывает трудно отладить, поэтому будьте готовы к трудностям.

Итак, если положительное решение принято и глобальные переменные все-таки нужны, то действуйте примерно по такой схеме:

1. Поместите все глобальные переменныев один static класс (для удобства управляемости кодом).
2. Используйте внутри этого класса обертки get/set для доступа к "глобальным" переменным. Это предоставит дополнительный механизм для отладки проблем, возникающих из-за конкурентного доступа.

Вот базовый пример для такого класса:

public class Globals
{
    private static bool _expired;
    public static bool Expired 
    {
        get
        {
            // Чтение обычно очень простое:
            return _expired;
        }
        set
        {
            // В этом месте можно добавить логику, отслеживающую
            // проблемы конкурентного доступа (race conditions),
            // вывод в лог или другие методы отладки.
            _expired = value;
        }
    }
    // По ситуации можно расширить этот пример static-методами Read-Modify-Write
    // (чтение-модификация запись) для поддержки целостности данных при 
    // конкурентном доступе (поддержка атомарности). Конкретное решение
    // зависит от Ваших потребностей.
    ...
}

Использование переменных этого класса из других классов (находящихся в том же пространстве имен namespace) может быть таким:

// Чтение:
bool areWeAlive = Globals.Expired;
 
// Запись:
Globals.Expired = true;

Хорошая информация по теме static классов C# есть в статье "Статика в C#", опубликованной на Хабрахабр (ключевые слова для поиска Статика в C# site:habrahabr.ru).

Пример определения дочернего класса TMyClass внутри родительского класса myform:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace myprog
{
   public partial class myform : Form
   {
      public class TMyClass
      {
         public TMyClass()
         {
            //Это конструктор дочернего класса
            ...
         }
         ...
      }
//Определение массива классов из 7 элементов: public TMyClass [] classarray = new TMyClass [7]; ... public myform() { //Это конструктор родительского класса InitializeComponent(); ... } //Вот так нужно правильно инициализировать массив классов TMyClass внутри // события загрузки формы (родительский класс myform). Альтернативно то же // самое можно выполнить в конструкторе родительского класса: private void myform_Load(object sender, EventArgs e) { for (int idx = 0; idx < 7; idx++) { //Вызов конструкторов TMyClass: classarray[idx] = new TMyClass(); } } } }

После этого можно обращаться к экземплярам классов TMyClass по индексу (вызывать его методы, обращаться к свойствам). Если не сделать инициализацию элементов массива, то при запуске программы и попытке доступа к экземплярам классов из массива получите ошибку "ссылка на объект не указывает на экземпляр объекта".

У меня была следующая проблема: KeyDown срабатывает нормально, пока не добавлена на форму кнопка. Как только кнопка добавлена, KeyDown перестает работать.

[Решение 1]

Нужно установить у формы свойство KeyPreview=true. Этот совет можно найти на большинстве форумов, однако он помогает не всегда - возможно, это зависит от версии библиотек Visual Studio C#. Мне не помогло (Microsoft Visual Studio 2010 Версия 10.0.40219.1 SP1Rel).

[Решение 2]

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

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
   switch (keyData)
   {
      case Keys.Space:
         //Действия при нажатии пробела:
         ...
         return true;
      case Keys.Left:
         //Действия при нажатии кнопки "влево":
         ...
         return true;
      case Keys.Right:
         //Действия при нажатии кнопки "вправо":
         ...
         return true;
      //Тут можно добавить обработку других нажатий...
      ...
   }
   return base.ProcessCmdKey(ref msg, keyData);
}

Обратите внимание, что при возврате true из метода ProcessCmdKey не будет отправлено событие нажатия на элемент управления, на котором установлен фокус.

Окно имеет высоту (свойство Height) и ширину (свойство Width), которые определяют размеры окна формы. Эти свойства можно читать или менять программно. Однако есть еще и "клиентская" область формы, которая меньше. По ширине она меньше на ширину бордюров окна, а по высоте она меньше еще и на размер верхней плашки (title bar).

[Свойство ClientSize]

У свойства ClientSize формы есть поля только для чтения - Width и Height, по значению которых можно определить размеры клиентской области формы (ширину и высоту соответственно).

[Метод SetClientSizeCore]

Метод SetClientSizeCore принимает 2 параметра - x и y, и позволяет установить размеры клиентской области формы.

Пример, в котором возникает ошибка (Невозможно получить доступ к нестатическому члену внешнего типа ...):

namespace myprog
{
   public partial class myform : Form
   {
      public int timestamp;
      ...
      public class TChildClass
      {
         public int time;
         public TChildClass()
         {
            time = timestamp;    //ошибка CS0038
         }
      }
      ...
   }
   ...
}

[Варианты решения проблемы]

1. Можно применить к переменной или методу, к которому нельзя получить доступ, атрибут static:

namespace myprog
{
   public partial class myform : Form
   {
      public static int timestamp;  //это устранит ошибку CS0038
      ...
      public class TChildClass
      {
         public int time;
         public TChildClass()
         {
            time = timestamp;
         }
      }
      ...
   }
   ...
}

2. Вместо вложенных классов (nesting) применить наследование (inheritance). Родительский класс должен иметь тип доступа public, чтобы можно было применить наследование:

public class myform : Form
{
    public int timestamp;
    ...
}
 
public class TChildClass : myform
{
   public class TChildClass
   {
      public int time;
      public TChildClass()
      {
         time = timestamp;
      }
   }
}

3. Создать экземпляр класса, который содержит нужную переменную или метод:

namespace myprog
{
   public class TParentClass
   {
      public int timestamp;
      ...
 
      public class TChildClass
      {
         public int time;
         public TChildClass()
         {
            TParentClass pc = new TParentClass();
            time = pc.timestamp;
         }
      }
      ...
   }
   ...
}

[Ссылки]

1. ASP.NET and Other Tips.
2. C#: работа с USB-CAN адаптером SYSTEC.
3. C#: обзор ComboBox.
4: C#: методы Show и ShowDialog.
5. C#: желтые всплывающие подсказки (hint).
6. C#: как узнать, что пользователь кликнул на кнопку 'X' формы?

 

Комментарии  

 
0 #8 Сергей 11.04.2015 02:38
Подскажите, как вывести поверх компонентов, находящихся на Form1 (panel, label и т. п.) Form2.
Цитировать
 
 
+1 #7 Robin 05.09.2014 16:30
При запуске программы появляется два окна: одно с путём к файлу проекта (находится над вторым окном), второе окно имеет путь C:WindowsSystem 32.cmd.exe. Первое окно висит около 14 секунд (в нём ничего не происходит и закрыть его нельзя), потом закрывается и во втором окне (с путём C:WindowsSystem 32.cmd.exe) появляются результаты работы программы. Почему появляется два окна? Как сделать, что было только одно окно?
Цитировать
 
 
0 #6 Максим 09.08.2014 04:34
Именно с открытием формы без каких-либо кнопок, как вывести на PictureBox последовательно сть изображений по кругу из Properties.Resources?

microsin: воспользуйтесь обработчиком таймера Timer или потоком BackgroundWorke r, и отображайте оттуда нужные картинки.
Цитировать
 
 
0 #5 Andrey 30.05.2014 03:55
[quote name="Александр"]При запуске Visual Studio 2010 появляется ошибка: инициализатор типа выдал исключение, и Visual Studio не запускается, в чем проблема?

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

Лечение - удалить или переименовать форму, загрузить Visual Studio и пересобрать форму из корректных элементов.
Цитировать
 
 
0 #4 Дмитрий 27.10.2013 19:33
Можно ли в listbox-e в одном элементе вывести несколько строк одна под одной?
Цитировать
 
 
0 #3 Андрей 29.12.2012 06:50
На форме есть Panel.
Как передать процедуре Panel1_Paint пару переменных? Глобальные переменные, определенные в
public partial class Form1 : Form {
почему-то в процедуре Panel1_Paint всегда равны 0.
Цитировать
 
 
0 #2 Александр 06.12.2012 19:13
При запуске Visual Studio 2010 появляется ошибка: инициализатор типа выдал исключение, и Visual Studio не запускается, в чем проблема?

microsin: думаю, что даже Microsoft не знает, в чем проблема. Просто переустановите Visual Studio, и ошибка исчезнет.
Цитировать
 
 
+1 #1 Павел 26.09.2012 09:11
Как вывести на форму значение переменной типа int?

microsin: сначала нужно перевести число в строку. Сделать это можно с помощью метода Convert (см. Q022), или с помощью преобразований Format. Затем нужно нарисовать полученный текст на форме. Можно это сделать с помощью метки (Label), брошенной на форму, а можно с помощью метода DrawString (см. Q034). Вариантов много.
Цитировать
 

Добавить комментарий


Защитный код
Обновить

Top of Page