Несколько вопросов по .NET и C# (ответы с разбором)

в 21:23, , рубрики: .net, метки: ,

На минувших выходных я запустил пост «Несколько вопросов по .NET и C#»

Я думаю многим интересно не только найти правильные ответы, но и узнать какая часть пользователей знает эти правильные ответы. Именно с этой целью и создавался этот пост.

Я не настаиваю на истинности объяснений в последней инстанции, поэтому конструктивная критика, дополнения и уточнения приветствуются.

image

Обещанные ответы с разбором под катом.

image
Этот вопрос наделал больше всего шума в комментариях.

В спецификации 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 о том, что инициализация статического поля произойдет до первого обращения к этому полю, причем она может произойти задолго до этого обращения.

Кстати, на этом свойстве статического конструктора построена одна из реализаций Singleton'а

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;
        }
    }
}

image

Для подобных целей в C# предусмотрены ключевые слова add и remove. Их необходимо использовать аналогично get и set для свойств, то есть:

public class MyClass {
  private EventHandler myEvent;
  public event EventHandler MyEvent
  {
    add { myEvent += value; }
    remove { myEvent -= value; }
  }
}

image

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 оператор.

image

Все, кроме директивы #typedef.

С помощью #define нельзя писать макросы, как, например, в С++, но можно устанавливать значения. Директивами #if, #else, #endif проверяется, установлено ли значение. Атрибут Conditional служит для указания компилятору компилировать или не компилировать метод, в зависимости от того установлено ли соответствующее значение #define.

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

#define TRACE_ON
using System;
using System.Diagnostics;

public class Trace
{
    [Conditional("TRACE_ON")]
    public static void Message(string msg)
    {
        Console.WriteLine(msg);
    }
}

image

В C# создавать вложенные пространства имен можно либо написав их имена через точку, либо вложив одно в другое с помощью составного оператора { }.

image

Чтобы использовать unsafe код необходимо вызывать компилятор с ключом /unsafe, этот ключ также можно установить в настройках проекта. Помимо этого каждый метод с unsafe кодом необходимо пометить ключевым словом unsafe.

image

Рассмотрим пример с кнопкой. Создав объект класса Button, мы подписываемся на его событие Click, которые происходит при нажатии на кнопку. В данном случае, объект Button является издателем (publisher), а тот метод, который подписан на событие, соответственно, подписчиком (subscriber).

image

Во время компиляции значения констант подставляются в места их использования.

image
Большая часть пользователей не знает правильного ответа на этот вопрос.

Все перечисленные элементы можно найти в типе-перечислении AttributeTargets, значения которого используется в атрибуте AttributeUsage, служащего для указания, что может быть помечено атрибутом.

Пример использования атрибута, примененного к возвращаемому значению:

using System.Runtime.InteropServices;

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean MessageBeep(UInt32 uType);

image

Существует специальное пространство System.Runtime.InteropServices, в котором находятся классы для работы с неуправляемым кодом, например, хорошо известный атрибут DllImport, для подключения DLL-функций.

image
Большая часть пользователей не знает правильного ответа на этот вопрос.

Атрибутом Obsolete отмечается нерекомендуемая для использования сущность программы. Каждый случай использования сущности, отмеченной устаревшей, будет приводить к генерированию предупреждения или ошибки в зависимости от настроек этого атрибута.

Автор: ZEEGIN

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js