﻿# Занимательный C\#

Для оценки качества диагностик анализатора C\# кода PVS\-Studio мы проверяем большое количество различных проектов\. Т\.к\. проекты пишутся разными людьми в различных командах в разных компаниях, нам приходится сталкиваться с различными стилями, сокращениями, да и просто возможностями, которые предлагает язык C\# программистам\. В этой статье я хочу обзорно пройтись по некоторым моментам, которые предлагает нам замечательный язык C\#, и по тем проблемам, на которые можно наткнуться при его использовании\. 

![0403_Amusing_CSharp_ru/image1.png](https://import.viva64.com/docx/blog/0403_Amusing_CSharp_ru/image1.png)

Ремарка\.

Данная статья больше ориентирована на любознательность и описывает те вещи, про которые мне лично показалось интересным рассказать\.

## Свойства и что с ними можно делать

Мы все знаем, что свойства \- это пара функций: аксессор и мутатор, для изменения и чтения значения в каком\-то поле\. Ну, или по крайней мере так было до версии языка C\# 3\.0\. Т\.е\. классически они должны выглядеть вот так:

```cpp
class A
{
  int index;
  public int Index
  {
    get { return index; }
    set { index = value; }
  }
}
```

Шли годы, и стандарты языка, и свойства обросли разными возможностями\.

Начнем понемногу\. В стандарте C\# 3\.0 появилась всем известная возможность опустить поле, т\.е\. записать так:

```cpp
class A
{
  public int Index { get; set; }
}
```

В C\# 6\.0 пошли еще дальше и позволили убрать "set"\.

```cpp
class A
{
  public int Index { get; }
}
```

Так писать можно было и до C\# 6\.0, но записать в такую переменную что\-либо было нельзя\. Теперь это, по факту, является аналогом _readonly_ полей, т\.е\. задавать значение таких свойств можно только в конструкторе\. 

Свойства и поля можно инициализировать различными способами\. Например, так:

```cpp
class A
{
  public List<int> Numbers { get; } = new List<int>(); 
}
```

Ну или так:

```cpp
class A
{
  public List<int> Numbers = new List<int>();
}
```

А еще можно написать так:

```cpp
class A
{
  public List<int> Numbers => new List<int>();
}
```

И в последнем случае вас будет ждать неприятный сюрприз\. На самом деле, в последнем примере вы создали вот такое вот свойство:

```cpp
class A
{
  public List<int> Numbers { get { return new List<int>(); } }
}
```

Т\.е\. когда вы попытаетесь заполнить _Numbers_, то у вас ничего не получится в принципе, каждый раз вы будете иметь новый список\. 

```cpp
A a = new A();
a.Numbers.Add(10);
a.Numbers.Add(20);
a.Numbers.Add(30);
```

Будьте внимательны, когда сокращаете запись, иногда это может привести к весьма долгому поиску ошибки\.

Интересные свойства свойств на этом не заканчиваются\. Как я уже сказал, свойство \- это пара функций, а в функциях никто не мешает менять параметры, которые туда приходят\.

Следующий код прекрасно компилируется и даже работает\.

```cpp
class A
{
  int index;
  public int Index
  {
    get { return index; }
    set { 
      value = 20; 
      index = value; }
  }
}
static void Main(string[] args)
{
  A a = new A();
  a.Index = 10;
  Console.WriteLine(a.Index);
}
```

Результатом работы будет вывод числа "20", а никак не "10"\.

Казалось бы, зачем кому\-то сдалось записывать значение 20 в _value_? Оказывается, даже в этом может быть смыл\. Но для пояснения этого смысла мы немного отвлекаемся от свойств и расскажем о ключевом символе @\. Данный ключевой символ позволяет создавать переменные схожие по написаю с ключевыми словами\. Например: _@this_, _@operator_ и т\.д\. Но никто не запрещает, данный символ, пихать куда душа пожелает, например:

```cpp
class A
{
  public int index;
  public void CopyIndex(A @this)
  {
    this.@index = @this.index;
  }
}
static void Main(string[] args)
{
  A a = new A();
  @a.@index = 10;
  a.@CopyIndex(new A() { @index = 20 });
  Console.WriteLine(a.index);
}
```

Результатом работы, как всегда в этой статье, будет вывод числа "20", а никак не "10"\.

На самом деле, символ _@_ необходим только в одном месте, когда пишем имя параметра _@this_ в функции _CopyIndex_\. В других местах это просто лишний код, который, к тому же, затрудняет понимание написанного\.

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

```cpp
class A
{
  int value;
  public int Value
  {
    get { return @value; }
    set { @value = value; }
  }
  public A()
  {
    value = 5;
  }
}
```

Можно подумать, что в свойстве _Value_ изменится поле _value_ класса _A_\. Но, на самом деле, так не произойдет, и результатом работы следующий программы будет 5, а не 10\.

```cpp
static void Main(string[] args)
{
  A a = new A();
  a.Value = 10;
  Console.WriteLine(a.Value);
}
```

Данное поведение происходит из\-за не соответствия _@value_ в _get_ и _@value_ в _set_\. _@value_ в get будет являться ничем иным, кроме как, полем класса _A_\. А _@value_ в _set_ на самом деле \- это параметр функции _set_\. Таким образом мы просто пишем _value_ само в себя и никак не затрагиваем поле _value_ в классе _А_\.

## Инициализация коллекций

Для начала вспомним различные способы инициализации массивов:

```cpp
string[] test1 = new string[] { "1", "2", "3" };
string[] test2 = new[] { "1", "2", "3" };
string[] test3 = { "1", "2", "3" };
string[,] test4 = { { "11", "12" }, 
                    { "21", "22" }, 
                    { "31", "32" } };
```

Со списками дела обстоят легче и есть только один вариант  инициализации:

```cpp
List<string> test2 = new List<string>(){ "1", "2", "3" };
```

Ну и напоследок _Dictionary_: 

```cpp
Dictionary<string, int> test = 
  new Dictionary<string, int>() { { "a-a", 1 }, 
                                  { "b-b", 2 }, 
                                  { "c-c", 3 } };
```

А вот ради следующего способа я и писал данный раздел, ибо его я видел впервые:

```cpp
Dictionary<string, int> test = 
  new Dictionary<string, int>() { 
    ["a-a"] = 1,
    ["b-b"] = 2,
    ["c-c"] = 3
  };
```

## Немного о LINQ запросах

LINQ запросы в принципе сама по себе вещь удобная\. Собираем цепочку с необходимыми выборками и на выходе получаем необходимую информацию\. Для начала опишем пару приятных моментов, которые могут не прийти в голову, пока сам не увидишь их\. Для начала рассмотрим базовый пример:

```cpp
void Foo(List<int> numbers1, List<int> numbers2) {
  var selection1 = numbers1.Where(index => index > 10);
  var selection2 = numbers2.Where(index => index > 10);
}
```

Нетрудно заметить, что в выше описанном примере есть несколько одинаковых проверок\. То есть по\-хорошему, их можно вынести в отдельную "функцию":

```cpp
void Foo(List<int> numbers1, List<int> numbers2) {
  Func<int, bool> whereFunc = index => index > 10;
  var selection1 = numbers1.Where(index => whereFunc(index));
  var selection2 = numbers2.Where(index => whereFunc(index));
}
```

Уже стало лучше, если функции большие, то вообще прекрасно\. Немного смущает вызов _whereFunc_: какой\-то он неказистый\. На самом деле, это тоже не проблема:

```cpp
void Foo(List<int> numbers1, List<int> numbers2) {
  Func<int, bool> whereFunc = index => index > 10;
  var selection1 = numbers1.Where(whereFunc);
  var selection2 = numbers2.Where(whereFunc);
}
```

Вот теперь и лаконично и опрятно\. 

Теперь немного о нюансах работы LINQ выражений\. Например, строчка кода не приведет к моментальной выборке данных из коллекции _numbers1_\.

```cpp
IEnumerable<int> selection = numbers1.Where(whereFunc);
```

Выборка данных начнется, только когда будет выполнена конвертация последовательности в коллекцию List<int\>:

```cpp
List<int> listNumbers = selection.ToList();
```

Этот нюанс работы может легко привести к использованию захваченной переменной уже после того, как её значение изменилось\. Возьмем простой пример\.  Допустим, нам нужна функция _Foo,_ которая вернет из массива "\{ 1, 2, 3, 4, 5 \}" только те элементы, численные значения которых меньше индекса элемента, т\.е:

```cpp
0 :
1 :
2 : 1
3 : 1, 2
4 : 1, 2, 3
```

Её сигнатура пусть будет такой:

```cpp
static Dictionary<int, IEnumerable<int>> Foo(int[] numbers)
{ ....  }
```

А вызов вот такой:

```cpp
foreach (KeyValuePair<int, IEnumerable<int>> subArray in 
           Foo(new[] { 1, 2, 3, 4, 5 }))
Console.WriteLine(string.Format("{0} : {1}", 
                  subArray.Key, 
                  string.Join(", ", subArray.Value)));
```



Всё вроде бы просто\. Теперь напишем саму реализацию на основе LINQ\.  Она будет выглядеть вот так:

```cpp
static Dictionary<int, IEnumerable<int>> Foo(int[] numbers)
{
  var result = new Dictionary<int, IEnumerable<int>>();
  for (int i = 0; i < numbers.Length; i++)
    result[i] = numbers.Where(index => index < i);
  return result;
}
```

Как можно видеть, всё предельно просто\. Мы берем и поочерёдно "создаем" выборки из массива _numbers_\.

Результатом работы такой программы будет вот такой текст в консоли:

```cpp
0 : 1, 2, 3, 4
1 : 1, 2, 3, 4
2 : 1, 2, 3, 4
3 : 1, 2, 3, 4
4 : 1, 2, 3, 4
```

Проблема тут как раз в замыкании, которое произошло в лямбде _index \=\> index < i_\. Переменная _i_ была захвачена, но, так как вызов лямбда выражения _index \=\> index < i_ не происходил до момента, когда мы попросили результат в функции _string\.Join\(", ", subArray\.Value\)_, значение в ней было не такое, как в момент формирования LINQ запроса\. Во время получения данных из выборки значения _i_ было равным 5, что привело к неверному результату вывода\.

## Недокументированные костыли на C\#

Язык С\+\+ известен своими хаками, обходными путями и прочими костылями, чего стоит серия функций _XXX\_cast_\. Считается, что в C\# такого нет\. На самом деле и это не совсем правда\.\.\. 

Начнем, пожалуй, с нескольких слов:

* _\_\_makeref_
* _\_\_reftype_
* _\_\_refvalue_

Этих слов нет ни в IntelliSense, да и в MSDN нет официального описания к ним\.

Так что это за чудо\-слова такие?

_\_\_makeref_ принимает объект и возвращает некую "ссылку" на объект в виде объекта типа _TypedReference_\. А, собственно, слова _\_\_reftype_ и _\_\_refvalue_ позволяют из этой "ссылки" узнать соответственно тип объекта и значение объекта по данной "ссылке"\.

Рассмотрим пример:

```cpp
struct A { public int Index { get; set; } }
static void Main(string[] args)
{
  A a = new A();
  a.Index = 10;
  TypedReference reference = __makeref(a);
  Type typeRef = __reftype(reference);
  Console.WriteLine(typeRef); //=> ConsoleApplication23.Program+A
  A valueRef = __refvalue(reference, A);
  Console.WriteLine(valueRef.Index); //=> 10
}
```

Но такой "финт ушами" можно сделать немного более известными средствами:

```cpp
static void Main(string[] args)
{
  A a = new A();
  a.Index = 10;
  dynamic dynam = a;
  Console.WriteLine(dynam.GetType());
  A valuDynam = (A)dynam;
  Console.WriteLine(valuDynam.Index);
}
```

С _dynamic_ и строк меньше, да и вопросов меньше должно вызывать у людей \- "Что это?" и "Как это работает?"\. Но вот вам немного иной сценарий, где работа с _dynamic_ смотрится не так хорошо, как с _TypedReference_\.

```cpp
static void Main(string[] args)
{
  TypedReference reference = __makeref(a);
  SetVal(reference);
  Console.WriteLine(__refvalue(reference, A).Index);
}
static void SetVal(TypedReference reference)
{
  __refvalue(reference, A) = new A() { Index = 20 };
}
```

Результатом работы будет вывод на консоль числа "20"\. Да, можно и _dynamic_ через _ref_ в функцию передать и работать будет также\. 

```cpp
static void Main(string[] args)
{
  dynamic dynam = a;
  SetVal(ref dynam);
  Console.WriteLine(((A)dynam).Index);
}
static void SetVal(ref dynamic dynam)
{
  dynam = new A() { Index = 20 };
}
```

На мой взгляд, вариант с _TypedReference_ выглядит лучше, особенно если прокидывать информацию всё ниже, и ниже, и ниже по функциям\.

Кроме выше описанных, есть еще одно чудо\-слово _\_\_arglist,_ которое позволяет сделать функцию с переменным числом параметров, да еще и любого типа\.

```cpp
static void Main(string[] args)
{
  Foo(__arglist(1, 2.0, "3", new A[0]));
}
public static void Foo(__arglist)
{
  ArgIterator iterator = new ArgIterator(__arglist);
  while (iterator.GetRemainingCount() > 0)
  {
    TypedReference typedReference = 
      iterator.GetNextArg();
    Console.WriteLine("{0} / {1}",
      TypedReference.ToObject(typedReference),
      TypedReference.GetTargetType(typedReference));
   }
}
```

Странным является то, что нельзя из коробки организовать проход по элементам с помощью _foreach_, да и напрямую к элементу из списка не обратиться\. Так что до С\+\+ или JavaScript с его _arguments_ не дотягивает\.:\)

```cpp
function sum() {
  ....
  for(var i=0; i < arguments.length; i++) 
    s += arguments[i]
}
```

## Заключение

В заключение хочется сказать, что и С\+\+ и C\# \- весьма свободные по грамматике языки, и тем самым с одной стороны удобны в использовании, но с другой не защищают от опечаток\. Есть укоренившееся мнение, что в С\# нельзя ошибаться так, как в С\+\+, – на самом деле это вовсе не так\. В данной статье приведены весьма интересные, на мой взгляд, возможности языка, но львиная доля ошибок в C\# состоит не в них, а при написании обычных индукций _if_, как, например, в проекте [Infragistics](https://pvs-studio.ru/ru/blog/posts/csharp/0375/)\.

```cpp
public bool IsValid
{
get {
  var valid = 
    double.IsNaN(Latitude) || double.IsNaN(Latitude) ||
    this.Weather.DateTime == Weather.DateTimeInitial;
  return valid;
 }
}
```

[V3001](https://pvs-studio.ru/ru/docs/warnings/v3001/) There are identical sub\-expressions 'double\.IsNaN\(Latitude\)' to the left and to the right of the '\|\|' operator\. WeatherStation\.cs 25

Внимание рассеивается чаще всего именно в таких моментах, а потом долгие поиски "непонятно чего непонятно где"\. Так что не упускайте возможность уберечь себя от ошибок с помощью анализатора кода [PVS\-Studio](https://pvs-studio.ru/ru/pvs-studio/)\.