На минувших выходных я запустил пост «Несколько вопросов по .NET и C#»
Я думаю многим интересно не только найти правильные ответы, но и узнать какая часть пользователей знает эти правильные ответы. Именно с этой целью и создавался этот пост.
Я не настаиваю на истинности объяснений в последней инстанции, поэтому конструктивная критика, дополнения и уточнения приветствуются.
Обещанные ответы с разбором под катом.
Этот вопрос наделал больше всего шума в комментариях.
В спецификации C# сказано:
The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
An instance of the class is created.
Any of the static members of the class are referenced.
Потому самый близкий к истине ответ: "Один раз при первом создании экземпляра класса или при первом обращении к статическим членам класса". Хотя в данном случае наверное переформулировка "… при первом упоминании" будет более уместна.
Дело в том, что бывают статические конструкторы явные (когда конструктор задан явно) и неявные (присваивание значений статическим свойствам). В том случае, когда нет явно заданного статического конструктора, класс помечается флагом beforefieldinit
, что говорит CLR о том, что инициализация статического поля произойдет до первого обращения к этому полю, причем она может произойти задолго до этого обращения.
public sealed class Singleton
{
static readonly Singleton instance = new Singleton();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Singleton()
{
}
Singleton()
{
}
public static Singleton Instance
{
get
{
return instance;
}
}
}
Для подобных целей в C# предусмотрены ключевые слова add и remove. Их необходимо использовать аналогично get
и set
для свойств, то есть:
public class MyClass {
private EventHandler myEvent;
public event EventHandler MyEvent
{
add { myEvent += value; }
remove { myEvent -= value; }
}
}
int i = 5;
object o = i;
long j = (long)o;
Большая часть пользователей не знает правильного ответа на этот вопрос.
Будет сгенерировано исключение InvalidCastException (Исключение, которое выбрасывается при недопустимом приведении или явном преобразовании типов). Ошибка находится в третьей строчке.
Приведение типов — это преобразование значения переменной одного типа в значение другого типа, бывает явным (explicit) и неявным (implicit):
int i = 123;
long l = (long)i; // explicit
int i = 123;
long l = i; // implicit
Существуют два вида типов: value-type и reference-type (типы-значения и типы-ссылки, соответственно), в тот момент, когда мы присваиваем переменной с ссылочным типом некоторое значение происходит boxing
(упаковка) этого значения.
Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type
Обратный процесс называется unboxing
(распаковка).
Для того чтобы исправить ошибку достаточно изменить третью строчку long j = (int)o
; Сначала будет произведен unboxing
и возвращена переменная типа int
, после чего будет вызван соответствующий implicit
оператор.
Все, кроме директивы #typedef.
С помощью #define
нельзя писать макросы, как, например, в С++, но можно устанавливать значения. Директивами #if
, #else
, #endif
проверяется, установлено ли значение. Атрибут Conditional
служит для указания компилятору компилировать или не компилировать метод, в зависимости от того установлено ли соответствующее значение #define
.
#define TRACE_ON
using System;
using System.Diagnostics;
public class Trace
{
[Conditional("TRACE_ON")]
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}
В C# создавать вложенные пространства имен можно либо написав их имена через точку, либо вложив одно в другое с помощью составного оператора { }
.
Чтобы использовать unsafe
код необходимо вызывать компилятор с ключом /unsafe, этот ключ также можно установить в настройках проекта. Помимо этого каждый метод с unsafe
кодом необходимо пометить ключевым словом unsafe.
Рассмотрим пример с кнопкой. Создав объект класса Button
, мы подписываемся на его событие Click
, которые происходит при нажатии на кнопку. В данном случае, объект Button
является издателем (publisher), а тот метод, который подписан на событие, соответственно, подписчиком (subscriber).
Во время компиляции значения констант подставляются в места их использования.
Большая часть пользователей не знает правильного ответа на этот вопрос.
Все перечисленные элементы можно найти в типе-перечислении AttributeTargets
, значения которого используется в атрибуте AttributeUsage
, служащего для указания, что может быть помечено атрибутом.
Пример использования атрибута, примененного к возвращаемому значению:
using System.Runtime.InteropServices;
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean MessageBeep(UInt32 uType);
Существует специальное пространство System.Runtime.InteropServices, в котором находятся классы для работы с неуправляемым кодом, например, хорошо известный атрибут DllImport
, для подключения DLL-функций.
Большая часть пользователей не знает правильного ответа на этот вопрос.
Атрибутом Obsolete
отмечается нерекомендуемая для использования сущность программы. Каждый случай использования сущности, отмеченной устаревшей, будет приводить к генерированию предупреждения или ошибки в зависимости от настроек этого атрибута.