Pull to refresh

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

Reading time 4 min
Views 131K
На минувших выходных я запустил пост «Несколько вопросов по .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 о том, что инициализация статического поля произойдет до первого обращения к этому полю, причем она может произойти задолго до этого обращения.

Кстати, на этом свойстве статического конструктора построена одна из реализаций 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;
        }
    }
}





Для подобных целей в 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 и #elseif.

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

Пример с использованием Conditional
#define TRACE_ON
using System;
using System.Diagnostics;

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

[Conditional("DEBUG")]
public void Test()
{
    MessageBox.Show("I want cookie");
}
ctor()
{
    this.Test();
    this.GetType().InvokeMember("Test", BindingFlags.InvokeMethod, null, this, null);
}


-UPD


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

+ADD
Про настройки проекта в вопросе не упомянуто, значит, берём поведение по умолчанию. Указание атрибута: [Obsolete]
На методе в соданном решении, без изменения свойств решения приведёт именно к предупрежению:
public ObsoleteAttribute()
{
    this._message = null;
    this._error = false;
}

-ADD

Подробно на MSDN



Структуры не поддерживают наследование, но могут реализовывать интерфейсы. Также одной из особенностей является невозможность определения конструктора по умолчанию, так как поля инициализируются значениями по умолчанию автоматически. Но, однако, возможно определение статического конструктора.
Tags:
Hubs:
+36
Comments 37
Comments Comments 37

Articles