c#

Home  /  Game Development  /  c#

c#

Разное
is as
Расширяющие методы
Делегаты
Обобщения
Атрибуты, рефлексия
Исключения
События
Потоки
Синхронизация доступа к 1 ресурсу из нескольких потоков
Коллекции
Анонимные типы
LINQ
Динамические типы
Перегрузка операторов


РАЗНОЕ
Псевдоним
using WintellectWidget = Wintellect.Widget; 
Неизменяемые поля
  • const
  • readonly
  • public int x { get; private set; }
  • Статический класс нужен для группировки логически связанных членов
  • partial класс позволяет разбить 1 класс на несколько частей, чтобы над каждой программисты могли работать отдельно
  • если класс не предназначен для наследования, нужно всегда запечатывать его — sealed
  • все поля класса всегда делать закрытыми
  • если класс слишком большой — разбить его на подклассы. если класс используется одним экземпляром — сделать его вложенным
  • если в базовом классе есть вирт метод, то в дочернем классе можно переопределить его через override, либо через new сказать, что переопределять не надо, а использовать метод дочернего класса как независящий от базового
  • константы в c# неявно статические, слово static к ним применять нельзя
  • readonly — запись в поле разрешается лишь из кода конструкторов
ref и out говорят, что аргумент надо передавать по ссылке и имеют лишь одно отличие:
  • refнужно инициализировать до вызова метода
  • out можно не инициализировать до вызова метода
Анонимные типы
var o = new { property1 = expression1, ..., propertyN = expressionN };
Когда вы пишете этот код, компилятор определяет тип каждого выражения, создает закрытые поля этих типов, для каждого типа поля создает открытые свойства только для чтения и для всех этих выражений создает конструктор. 
Индексаторы — свойства с параметрами
В классе может быть более одного индексатора, но они должны отличаться типом, или количествои индексов (как в перегрузке методов, но без типа возвращаемого значения, т.к. его нет у индексаторов)
class A
{
    private int[] arr = new int[5];
    public int this[int i]
    {
        get { return arr[i]; }
        set { arr[i] = value; }
    }
}
class Program
{
    static void Main()
    {
        A obj = new A();
        obj[0] = 0;
        obj[1] = 1;
        Console.WriteLine(obj[0]);
        Console.WriteLine(obj[1]);
    }
}
  • Использовать структуры для хранения переменных
  • Ключевое слово base/this в конструкторе дочернего класса
Полиморфизм
  • через upcast/downcast — приведение экземпляра к экземпляру базового/дочернего класса
  • через виртуальные методы
sealed
если применить к классу — запрещаем наследоваться от класса
если применить к override-методу — запрещаем дальнейшее переопределение
override можно применять к виртуальным и абстрактным методамg
Константные поля по своему поведению как статические. К обоим обращаемся так [имя класса].[имя поля]
Структуры
  • В структурах можно только объявлять поля, но нельзя инициализировать
Упаковка, распаковка
Распаковка должна производиться только в тот тип, из которого производилась упаковка
    short a = 5;
    object o = a;       // упаковка значимого типа в ссылочный
    short b = (short)o; // распаковка
Перечисления
enum MyEnum : int
{
    a = 0,
    b = 5,
    c,
}
Console.WriteLine((int)MyEnum.c);
Массивы
Double[,] myDoubles = new Double[10, 20];
String[,,] myStrings = new String[5, 3, 10];
Создание одномерного массива из массивов типа:
Point Point[][] myPolygons = new Point[3][];
Приведение типов в массивах:
FileStream[,] fs2dim = new FileStream[5, 10];
Object[,] o2dim = fs2dim;
Stream[,] s2dim = (Stream[,]) o2dim;
null
int? x = null;
int y = x ?? 5; // если левый операнд != null, то вернется он, иначе - правый

IS AS

Приведение типов в C# с помощью операторов is и as 
Например, оператор is проверяет совместимость объекта с данным типом, а в качестве результата выдает значение типа Boolean (true или false). Оператор is никогда не генерирует исключение.
Object o = new Object(); 
Boolean b1 = (o is Object);   // b1 равно true  
Boolean b2 = (o is Employee); // b2 равно false 
Для null-ссылок оператор is всегда возвращает false, так как объекта, тип которого нужно проверить, не существует. Обычно оператор is используется следующим образом: 
if (o is Employee) 
{   
    Employee e = (Employee) o;   // Используем внутри инструкции if  
}
Механизм, повышающий эффективность кода с помощью оператора as: 
Employee e = o as Employee; 
if (e != null) 
{   
    // Используем внутри инструкции if  
} 
В этом коде CLR проверяет совместимость o с типом Employee. Если o и Employee совместимы, as возвращает ненулевой указатель на этот объект, а если нет — оператор as возвращает null.
Оператор as отличается от оператора приведения типа только тем, что никогда не генерирует исключение
is
Если А — базовый класс, а В — дочерний:
    A a = new A();
    B b = new B();
    if (a is A) // true
    if (b is B) // true
    if (a is B) // false
    if (b is A) // true
as
а — экземпляр класса А, b — экземпляр класса В.
Если в можно привести к классу А (можно, если В наследуется от А), то в а запишется в. Иначе запишется null
a = b as A
as используется для безопасного upcast/downcast

РАСШИРЯЮЩИЕ МЕТОДЫ

  • Имеют слово this (только) перед первым аргументом
  • Могут быть только статическими и только в статических классах
  • Запрещаются параметры по умолчанию
  • Можно перегружать
  • Первый аргумент не может быть помечен словами ref, out, остальные могут
static class A
{
    public static void Extention(this string value)
        { Console.WriteLine(value); }
 
    public static void Extention(this string value1, string value2)
        { Console.WriteLine(value1 + value2); }
} 
class Program
{
    static void Main()
    {
        string text = "1";
        text.Extention();
        text.Extention("2");
        "1".Extention("2");
    }
}

ДЕЛЕГАТЫ
static class A
{
    public static void Display1()
        { Console.WriteLine("1"); }
 
    public static void Display2()
        { Console.WriteLine("2"); }
}
public delegate void Del();
class Program
{
    static void Main()
    {
        Del del1 = new Del(A.Display1);
        Del del2 = new Del(A.Display2);
        Del del3 = del1 + del2;
        del3();
    }
}
Анонимные методы
public delegate int Del(int a, int b);
class Program
{
    static void Main()
    {
        // Создаем экземпляр делегата и сообщаем ему анонимный метод
        Del del = delegate(int a, int b) { return a + b; };
        Console.WriteLine(del(1, 2));
    }
}
Лямбда-методы/выражения/операторы
delegate int Del(int val);
class Program
{
    static void Main()
    {
        Del del;
        // 3 варианта одного и того-же:
 
        // лямбда-метод. . Выражений может быть несколько
        del = delegate (int val) { Console.WriteLine("0"); return val * 2; };
 
        // лямбда-оператор. Выражений может быть несколько
        del = (val) => { Console.WriteLine("0"); return val * 2; };
 
        // лямбда-выражение. 1е значение - аргумент
        // 2е - возвращаемое значение. Выражение может быть только одно
        del = val => val * 2;
        
        Console.WriteLine(del(5));
    }
}
Лямбда-операторы, не принимающие никаких значений
delegate int Del();
del = delegate { return 1; };
del = () => { return 1; }
del = () => return 1;

ОБОБЩЕНИЯ
class A <T>
{
    public void Display<B>() { }
}
 
class Program
{
    static void Main()
    {
        A<int> obj = new A<int>();
        obj.Display<int>();
    }
}
Другой пример:
class Program
{
    static void Main()
    {
        A<bool> obj = new A<bool>();
        obj.Display<bool>(true);
    }
}
class A<T>
{
    T x;
    
    public void Display<B>(B y)
        { Console.WriteLine(x.GetType() + " " + y.GetType()); }
}
Обобщенный делегат
delegate R Del<R, T>(T val);
class Program
{
    static int Displ(int val)
    {
        Console.WriteLine(val);
        return 0;
    }
    static void Main()
    {
        Del<int, int> del = new Del<int, int>(Displ);
        del(1);
    }
}

АТРИБУТЫ, РЕФЛЕКСИЯ
Первый аргумент — область применения атрибута, второй — возможность указать несколько раз подряд:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
Для создания атрибута наследуемся от класса System.Attribute к имени класса-атрибута обязательно приписываем суффикс Attribute:
class ZAttribute : System.Attribute
{
    private readonly string date;
    public string Date
    {
        get { return date; }
    }
    // здесь указываем позиционные параметры атрибута - аргументы конструктора
    // для этих параметров последовательность важна
    public ZAttribute(string date)
    {
        this.date = date;
    }
    private int number;
    // здесь указываем именованные параметры - открытые нестатические свойства
    // для этих параметров последовательность не важна
    public int Number
    {
        get { return number; }
        set { number = value; }
    }
}
 
[Z("1/1/1", Number = 1)] //это конструктор класса ZAttribute
class A
{
    // инициализируем поле date и свойство Number
    // необязательно указывать полное имя арибута, слово Atribute можно не писать
    [Z("2/2/2", Number = 5)]
    public static void Method()
    {
        Console.WriteLine("0");
    }
}
class Program
{
    static void Main()
    {
        A objA = new A();
        A.Method();
        // Для работы с содержимым атрибутов нужна рефлексия
        Type type = typeof(A);
        //----------------------------------------------------------------
        //Получаем все атрибуты типа ZAttribute. false - без учета базовых классов
        object[] attributes = type.GetCustomAttributes(false);
        foreach(ZAttribute attr in attributes)
            Console.WriteLine(attr.Number + " " + attr.Date);
        //----------------------------------------------------------------
        // Получаем все атрибуты метода Method в типе ZAttribute
        // Получаем public static метод Method
        MethodInfo method = type.GetMethod("Method", BindingFlags.Public | 
                                                     BindingFlags.Static);
        attributes = method.GetCustomAttributes(typeof(ZAttribute), false);
        foreach (ZAttribute attr in attributes)
            Console.WriteLine(attr.Number + " " + attr.Date);
    }
}
Явное указание, к чему применяется атрибут
[assembly: AssemblyVersion("")]
[module: AssemblyVersion("")]
[type: AssemblyVersion("")]
[field: AssemblyVersion("")]
[return: AssemblyVersion("")]
[method: AssemblyVersion("")]
[param: AssemblyVersion("")]
[property: AssemblyVersion("")]
[event: AssemblyVersion("")]

ИСКЛЮЧЕНИЯ
Простейшее исключение
   static void Main()
    {
        try
        {
            int x = 1, y = 0;
            int z = x / y;
        }
        catch(Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
Длинная запись
    static void Main()
    {
        Exception ex = new Exception("123");
        try
        {
            throw ex;
        }
        catch(Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
Короткая запись
    static void Main()
    {
        Exception ex = new Exception("1");
        try
        {
            throw new Exception("2");
        }
        catch(Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
Исключение выводит дополнительную системную информацию
    static void Main()
    {
        Exception ex = new Exception("1");
        ex.HelpLink = "http://google.com";
        ex.Data.Add("Причина исключения: ", "Тестовое исключение");
        ex.Data.Add("Время возникновения исключения: ", DateTime.Now);
        try
        {
            throw new Exception("2");
        }
        catch(Exception e)
        {
            Console.WriteLine(e.Message);
            Console.WriteLine(e.TargetSite);
            Console.WriteLine(e.Source);
            Console.WriteLine(e.StackTrace);
        }
    }
Пользовательское исключение
class A : Exception { }
class Program
{
    static void Main()
    {
        try
        {
            throw new A();
        }
        catch(A e)
        {
            Console.WriteLine("Message: " + e.Message);
            Console.WriteLine("TargetSite: " + e.TargetSite);
            Console.WriteLine("Source: " + e.Source);
            Console.WriteLine("StackTrace: " + e.StackTrace);
        }
    }
}
finally
finally выполняется всегда — было/не было исключения. После возникновения исключения из блока try  сразу компилятор перепрыгивает в блок catch и код, который находится в catch за местом возникновения исключения не выполнится. Поэтому такой код помещают в finally.
    static void Main()
    {
        try
        {
            int x = 1, y = 0;
            int z = x / y;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        finally
        {
            Console.WriteLine("0");
        }
    }
Не стоит перехватывать все исключения (типа Exception). Лучше указать конкретное
    System.Exception 
    System.AggregateException  
    System.ApplicationException 
    System.Reflection.InvalidFilterCriteriaException 
    System.Reflection.TargetException 
    System.Reflection.TargetInvocationException 
    System.Reflection.TargetParameterCountException 
    System.Threading.WaitHandleCannotBeOpenedException 
    System.Diagnostics.Tracing.EventSourceException 
    System.InvalidTimeZoneException 
    System.IO.IsolatedStorage.IsolatedStorageException 
    System.Runtime.CompilerServices.RuntimeWrappedException 
    System.SystemException 
    System.Threading.AbandonedMutexException 
    System.AccessViolationException 
    System.Reflection.AmbiguousMatchException 
    System.AppDomainUnloadedException 
    System.ArgumentException 
    System.ArgumentNullException 
    System.ArgumentOutOfRangeException 
    System.Globalization.CultureNotFoundException 
    System.Text.DecoderFallbackException 
    System.DuplicateWaitObjectException 
    System.Text.EncoderFallbackException 
    System.ArithmeticException 
    System.DivideByZeroException 
    System.NotFiniteNumberException 
    System.OverflowException 
    System.ArrayTypeMismatchException 
    System.BadImageFormatException 
    System.CannotUnloadAppDomainException 
    System.ContextMarshalException 
    System.Security.Cryptography.CryptographicException 
    System.Security.Cryptography.CryptographicUnexpectedOperationException 
    System.DataMisalignedException 
    System.ExecutionEngineException 
    System.Runtime.InteropServices.ExternalException 
    System.Runtime.InteropServices.COMException 
    System.Runtime.InteropServices.SEHException 
    System.FormatException 
    System.Reflection.CustomAttributeFormatException 
    System.Security.HostProtectionException
    System.Security.Principal.IdentityNotMappedException 
    System.IndexOutOfRangeException 
    System.InsufficientExecutionStackException 
    System.InvalidCastException 
    System.Runtime.InteropServices.InvalidComObjectException 
    System.Runtime.InteropServices.InvalidOleVariantTypeException 
    System.InvalidOperationException 
    System.ObjectDisposedException 
    System.InvalidProgramException 
    System.IO.IOException 
    System.IO.DirectoryNotFoundException 
    System.IO.DriveNotFoundException 
    System.IO.EndOfStreamException 
    System.IO.FileLoadException 
    System.IO.FileNotFoundException 
    System.IO.PathTooLongException 
    System.Collections.Generic.KeyNotFoundException 
    System.Runtime.InteropServices.MarshalDirectiveException 
    System.MemberAccessException 
    System.FieldAccessException 
    System.MethodAccessException 
    System.MissingMemberException 
    System.MissingFieldException 
    System.MissingMethodException 
    System.Resources.MissingManifestResourceException 
    System.Resources.MissingSatelliteAssemblyException 
    System.MulticastNotSupportedException 
    System.NotImplementedException 
    System.NotSupportedException 
    System.PlatformNotSupportedException 
    System.NullReferenceException 
    System.OperationCanceledException 
    System.Threading.Tasks.TaskCanceledException 
    System.OutOfMemoryException 
    System.InsufficientMemoryException 
    System.Security.Policy.PolicyException 
    System.RankException 
    System.Reflection.ReflectionTypeLoadException 
    System.Runtime.Remoting.RemotingException 
    System.Runtime.Remoting.RemotingTimeoutException 
    System.Runtime.InteropServices.SafeArrayRankMismatchException 
    System.Runtime.InteropServices.SafeArrayTypeMismatchException 
    System.Security.SecurityException 
    System.Threading.SemaphoreFullException 
    System.Runtime.Serialization.SerializationException 
    System.Runtime.Remoting.ServerException 
    System.StackOverflowException 
    System.Threading.SynchronizationLockException 
    System.Threading.ThreadAbortException 
    System.Threading.ThreadInterruptedException 
    System.Threading.ThreadStartException 
    System.Threading.ThreadStateException
    System.TimeoutException 
    System.TypeInitializationException 
    System.TypeLoadException 
    System.DllNotFoundException 
    System.EntryPointNotFoundException 
    System.TypeAccessException 
    System.TypeUnloadedException 
    System.UnauthorizedAccessException 
    System.Security.AccessControl.PrivilegeNotHeldException 
    System.Security.VerificationException 
    System.Security.XmlSyntaxException 
    System.Threading.Tasks.TaskSchedulerException 
    System.TimeZoneNotFoundException

СОБЫТИЯ
Объявляется словом event
Каждому событию назначается
  • область действия, обычно public
  • тип делегата
  • имя
public event EventHandler<NewMainEventArgs> NewMail;
// delegate
public delegate void EventHandler<TEventArgs> (object sender, TEventArgs e) 
                                                    where TEventArgs : EventArgs;
// method prototype
void MethodName(object sender, NewMainEventArgs e) { }
Другой пример
public delegate void del();
public class A
{
    public event del ev = null;  // создаем событие    
    // здесь можно проверять, кто вызывает событие
    public void InvokeEvent()
        { ev.Invoke(); }
}
class B
{
    static private void Message0()
        { Console.WriteLine("0"); }
 
    static private void Message1()
        { Console.WriteLine("1"); }
 
    static void Main()
    {
        A objA = new A();
        // присваиваем событию делегат, на который подписываем метод
        objA.ev += new del(Message0);   // смысл ..
        objA.ev += Message1;                    // .. одинаковый
        objA.InvokeEvent(); // напрямую запрещено вызывать события так: objA.ev.Invoke()
    }
}
add remove
public delegate void del();
public class A
{
    public del ev = null;
    // Как в свойствах, здесь можно включить дополнительные проверки
    public event del Event
    {
        add { ev += value; }
        remove { ev -= value; }
    }
    public void InvokeEvent()
    {
        ev.Invoke();
    }
}
class B
{
    private static void Method0()
        { Console.WriteLine("0"); }
 
    private static void Method1()
    { Console.WriteLine("1"); }
 
    static void Main()
    {
        A objA = new A();
        objA.Event += Method0;
        objA.Event += Method1;
        objA.Event += delegate { Console.WriteLine("Anonimnuy method"); };
        // Отписать анонимный метод нельзя, код ниже не сработает
        objA.Event -= delegate { Console.WriteLine("Anonimnuy method"); };
        objA.InvokeEvent();
    }
}

ПОТОКИ
using System;
using System.Threading;
class A
{
    // Выполняем метод во вторичном потоке, запустив его из первичного
    // Метод ничего не принимает и не возвращает
    static void M0()
    {
        while(true)
            Console.WriteLine("M0");
    }
    static void Main()
    {
        // M0 - метод, который мы будем выполнять в отдельном потоке
        ThreadStart ts = new ThreadStart(M0);
 
        //  Сюда передаем делегат ts, на который подписан метод M0,
        // который мы запустим в отдельном потоке
        Thread thread = new Thread(ts);
 
        // Запуск метода M0 в первичном потоке
        thread.Start();
 
        // Выполняем во втором потоке
        while (true)
            Console.WriteLine("0");
    }
}
Локальные переменные в методах, вызываемых  в отдельных потоках
class A
{
    // Выполним этот метод одновременно в первичном
    // и вторичном потоках
    static void M0()
    {
        // Для каждого потока создается локальная
        // копия этой переменной
        int counter = 0;
 
        while(counter < 10)
        {
            Thread.Sleep(500); // Задержка потока на 0.5 сек
            Console.WriteLine("M0 " + counter);
            counter++;
        }       
    }
 
    static void Main()
    {
        // ThreadStart прописывать необязательно
        Thread thread = new Thread(M0); // запускаем во вторичном потоке
        thread.Start();
 
        M0(); // запускаем в первичном потоке
    }
}
Ссылка на экземпляр текущего потока
    static void M0()
    {
        // Thread.CurrentThread - ссылка потока на самого себя
        // Так мы сможем управлять потоком из самого себя, а не только из первичного потока
        Thread t1 = Thread.CurrentThread;
        t1.Name = "Secondary"; // присваиваем потому имя
        Console.WriteLine(t1.Name + " " + t1.GetHashCode());
    }
Передача структуры в метод вторичного потока
struct B
{
    public B(int a, string s) { this.a = a; this.s = s; }
 
    public int a;
    public string s;
    //..
}
class A
{
    // Если метод вторичного потока принимает аргумент,
    // то он должен быть типа object
    static void M0(object argument)
    {
        B objB = (B)argument;
        Console.WriteLine(objB.a + " " + objB.s);
    }
    static void Main()
    {
        // Для передачи аргумента в метод вторичного потока создаем
        // экземпляр данного класса
        ParameterizedThreadStart pts = new ParameterizedThreadStart(M0);
        Thread t0 = new Thread(pts);
        t0.Start(new B(5, "abc")); // создаем и передаем экземпляр класса B
    }
}
Асинхронный анонимный метод
class A
{
    static void Main()
    {
        int x = 0;
        // Асинхронный (в новом потоке) запуск анонимного метода
        Thread thread = new Thread(delegate()
           { Console.WriteLine(++x /*из анонимного метода обращаемся к переменной x*/);});
        thread.Start();
 
        // Задержка, чтобы код выше (из другого потока) успел выполниться
        // С маленькой задержкой, или без нее, сначала выполнится код ниже,
        // т.к. потоку нужно время для выполнения
        Thread.Sleep(100);
        Console.WriteLine(x);
    }
}
Завершение вторичного потока
class A
{
    static void M0()
    {
        while(true)
        {
            Console.WriteLine("Secondary");
            Thread.Sleep(500);
        }
    }
 
    static void Main()
    {
        ThreadStart ts = new ThreadStart(M0);
        Thread thread = new Thread(ts);
        thread.Start();
 
        for(int i = 0; i < 5; i++0)
        {
            Console.WriteLine("Primary");
            Thread.Sleep(500);
        }                
        
        // Завершение вторичного потока
        thread.IsBackground = true;
    }
}
Фоновый и активный поток
Потоки делятся на активные и фоновые. При завершении активных потоков CLR принудительно завершает также все запущенные на этот момент фоновые потоки, а завершение фоновых потоков происходит немедленно. Следовательно, активные потоки имеет смысл использовать для исполнения заданий, которые обязательно требуется завершить
public static class Program
{
    public static void Main()
    {    
        // Создание нового потока (по умолчанию активного)    
        Thread t = new Thread(Worker);
        // Превращение потока в фоновый    
        t.IsBackground = true;
        t.Start(); // Старт потока     
                   // В случае активного потока приложение будет работать около 10 секунд     
                   // В случае фонового потока приложение немедленно прекратит работу    
        Console.WriteLine("Returning from Main");
    }
    private static void Worker()
    {
        Thread.Sleep(10000); // Имитация 10 секунд работы
                             // Следующая строка выводится только для кода,      
                             // исполняемого активным потоком    
        Console.WriteLine("Returning from Worker");
    }
} 

СИНХРОНИЗАЦИЯ ДОСТУПА К 1 РЕСУРСУ ИЗ НЕСКОЛЬКИХ ПОТОКОВ
class B
{
    // Создаем объект блокировки
    object block = new object(); 
    // Будем запускать этот метод во вторичном потоке
    public void M0()
    {
        // Получаем хеш текущего потока
        int hash = Thread.CurrentThread.GetHashCode();        
        // Пока один поток не завершит работу внутри данного блока,
        // другой поток не сможет начать работать в нем
        lock(block)
        {
            for(int i = 0; i < 5; i++)
            {
                Console.WriteLine(i + " " + hash);
                Thread.Sleep(500);
            }
            Console.WriteLine("---");
        }
    }
} 
class A
{
    static void Main()
    {
        B objB = new B();        
        // Создадим и запустим три потока
        for(int i = 0; i < 3; i++)
        {
            new Thread(objB.M0).Start();
        }
    }
}
2й способ
class B
{
    object block = new object();
    public void M0()
    {
        int hash = Thread.CurrentThread.GetHashCode();
        // Ниже должна быть строка Monitor.Exit(block)
        Monitor.Enter(block);
        for(int i = 0; i < 5; i++)
        {
            Console.WriteLine(i + " " + hash);
            Thread.Sleep(500);
        }
        Console.WriteLine("---");
        // Если забыть указать эту строку, то первый поток выполнится,
        // а все остальные будут ждать завершения первого и никогда не 
        // начнут выполняться. Поэтому лучше использовать lock
        Monitor.Exit(block);
    }
}
 
class A
{
    static void Main()
    {
        B objB = new B();
        for(int i = 0; i < 3; i++)
            new Thread(objB.M0).Start();
    }
}
Выполнение 2х потоков
class A
{
    // Объект блокировки здесь один, но используется в 2х местах
    // которые выполнятся синхронно между собой
    static object locker = new object();
    static void M0()
    {
        // 1е место
        for(int i = 0; i < 5; i++)
            lock (locker)
            {
                Console.WriteLine("Secondary");
                Thread.Sleep(100);
            }    
    }
    
    static void Main()
    {
        ThreadStart ts = new ThreadStart(M0);
        Thread t = new Thread(ts);
        t.Start();
        // 2е место
        for (int i = 0; i < 5; i++)
            lock (locker)
            {
                Console.WriteLine("Primary");
                Thread.Sleep(100);
            }
    }
}

КОЛЛЕКЦИИ
Итератор и енумератор — одно и то-же.
// Для работы с коллекциями нужны 2 интерфейса
class A : IEnumerable, IEnumerator
{
    B[] b = null;
    int position = -1; // указатель текущей позиции элемента в коллекции
 
    public void InitCollection()
    {
        b = new B[4];
        b[0] = new B("string1", 1, 2);
        b[1] = new B("string1", 3, 4);
        b[2] = new B("string1", 5, 6);
        b[3] = new B("string1", 7, 8); 
    }
 
    //==============================================
    // Реализация интерфейса IEnumerable 
    IEnumerator IEnumerable.GetEnumerator()
        { return this as IEnumerator; } 
    //==============================================
    // Реализация IEnumerator     
    // Передвигаем указатель position на одну позицию
    public bool MoveNext()
    {
        if(position < b.Length - 1)
        {
            position++;
            return true;
        }
        else
            return false;
    }
    // Передвигаем position в начало коллекции
    public void Reset()
        { position = -1; } 
    // Получаем текцщий элемент коллекции
    public object Current
        { get { return b[position]; } }
}

АНОНИМНЫЕ ТИПЫ
Анонимные типы позволяют инкапсулировать набор свойств только для чтения в один объект без необходимости явного определения типа. Используются вместе с LINQ
    static void Main()
    {
        // Создание класса и 2х свойств в нем только для чтения
        var instance = new { Name = "Dima", Age = 25 };
        Console.WriteLine(instance.Name + " " + instance.Age);
 
        // Получение имени класса
        Type type = instance.GetType();
        Console.WriteLine(type.ToString());
 
        // Анонимный тип, вложенный в анонимный тип
        var instance2 = new { Number0 = 5, Id = new { Number1 = 7 } };
    }
Свойству анонимного типа присваиваем ссылку на экземпляр класса
class B
{
    public int var;
    public void Display() { Console.WriteLine(var); }
}
 
class A
{
    static void Main()
        { var instance = new { Obj = new B() }; }
}
Другой пример
class B
{
    public int var;
    public void Display() { Console.WriteLine(var); }
}
 
class A
{
    static void Main()
    {
        // Создаем экземпляр анонимного типа
        new
        {
            // Инициализаруем переменную поля B
            Obj = new B { var = 1 }
        }.Obj.Display(); // Через свойство Obj обращаеся к методу
    }
}
Анонимный тип и делегат
delegate void D(string @string);
class A
{
    static void Main()
    {
        var instance = new
            { Del = new D((string @string) => Console.WriteLine(@string)) };
        instance.Del.Invoke("Hello");
    }
}

LINQ
public class B
{
    public string StrVar { get; set; }
    public int IntVar { get; set; }
}

class A
{
    static void Main()
    {
        // БД, состоящая из анонимных типов, в которых находятся
        // свойства только для чтения
        var b = new List<B>
        {
            new B { StrVar = "0", IntVar = 0 },
            new B { StrVar = "1", IntVar = 1 },
            new B { StrVar = "2", IntVar = 2 },
        };

        //==========================================
        // Обращаемся к БД по запросу LINQ
        var query1 =
            from objB in b
            where objB.IntVar > 0
            orderby objB.StrVar, objB.IntVar
            select new
            {
                FirstVariable = objB.StrVar,
                SecondVariable = objB.IntVar
            };

        foreach(var i in query1)
            Console.WriteLine(i.FirstVariable + " " + i.SecondVariable);

        //==========================================
        // Обращаемся к БД через расширяющие методы
        var query2 =
            b
            .Where(objB => objB.IntVar > 0)
            .OrderBy(objB => objB.StrVar)
            .OrderBy(objB => objB.IntVar)
            .Select(objB => new
            {
                FirstVariable = objB.StrVar,
                SecondVariable = objB.IntVar
            });

        foreach (var i in query2)
            Console.WriteLine(i.FirstVariable + " " + i.SecondVariable);

        //==========================================
        // Обращаемся к БД через статические расширяющие методы
        var query3 =
            Enumerable.Select(
            Enumerable.OrderBy(
            Enumerable.OrderBy(
            Enumerable.Where(
                b, objB => objB.IntVar > 0),
                objB => objB.IntVar),
                objB => objB.StrVar),
                objB => new
                {
                    FirstVariable = objB.StrVar,
                    SecondVariable = objB.IntVar
                });
        
        foreach (var i in query3)
            Console.WriteLine(i.FirstVariable + " " + i.SecondVariable);
    }
}

================================================================================

    static void Main()
    {
        // Таблица умножения
        var query = from x in Enumerable.Range(1, 9)    // работает как два..
                    from y in Enumerable.Range(1, 10)   // .. вложенных цикла
                    select new
                    {
                        X = x,
                        Y = y,
                        Result = x * y
                    };

        foreach(var i in query)
            Console.WriteLine(i.X + " " + i.Y + " " + i.Result);
    }

================================================================================

    static void Main()
    {
        ArrayList al = new ArrayList();
        al.Add(0);
        al.Add(1);
        al.Add(2);

        // Здесь явно указываем тип int
        var query = from int i in al
                    select i * 2;
    }

================================================================================

Join

    static void Main()
    {
        var b = new List<B>
        {
            new B { Id = 1, Name = "Name1" },
            new B { Id = 2, Name = "Name2" },
            new B { Id = 3, Name = "Name3" },
        };

        var c = new List<C>
        {
            new C { Id = 4, Job = "Job1" },
            new C { Id = 2, Job = "Job2" },
            new C { Id = 3, Job = "Job3" },
        };

        // Сюда войдут записи, где в обоих коллекциях совпадает Id
        var query = from collectionB in b
                    join collectionC in c
                    on collectionB.Id equals collectionC.Id
                    orderby collectionB.Name descending // descending - по убыванию
                                                        // ascending - по возрастанию
                    select new
                    {
                        Id = collectionB.Id,
                        Name = collectionB.Name,
                        Job = collectionC.Job,
                    };

        foreach(var i in query)
            Console.WriteLine(i.Id + " " + i.Name + " " + i.Job);
    }

Порядок выполнения запросов

class B
{
    public B(int id, int number)
    {
        this.Id = id;
        this.Number = number;
    }

    public int Id { get; set; }
    public int Number { get; set; }
}

class A
{
    static void Main()
    {
        int[] numbers = { 1, 2, 3, 4, 5 };

        var query = from i in numbers
                    select new B(i, i * 2);

        // Первым значением массива будет 777, а не 1, т.к.
        // запросы выполняется при его вызове, а не при определении
        numbers[0] = 777;

        // Здесь происходит вызов запроса
        foreach(var i in query)
            Console.WriteLine(i.Id + " " + i.Number);
    }
}

Динамические типы

У динамических типов тип может меняться по ходу выполнения программы.

Могут быть:

  • статическими
  • типами аргументов, возвращаемого значения,  свойств, массивов и еще много чем..
  • событие НЕ может иметь динамический тип, только delegate
  • при перегрузке оператора с 1-2 аргументами один из аргументов должен быть НЕ динамическим
    static void Main()
    {
        dynamic var = 1;
        Console.WriteLine(var);
        var = "Hello";
        Console.WriteLine(var);
        var = DateTime.Now;
        Console.WriteLine(var);
    }

ПЕРЕГРУЗКА ОПЕРАТОРОВ

Перегрузка ToString и GetHashCode:

class B
{
    public override string ToString()
        { return "Hello"; }

    public override int GetHashCode()
        { return 57; }
}

class A
{
    static void Main()
    {
        B b = new B();
        Console.WriteLine(b.ToString());
        Console.WriteLine(b.GetHashCode());
    }
}

Перегрузка Equals:

class B
{
    protected int x, y;
    
    public B(int X, int Y)
    {
        this.x = X;
        this.y = Y;
    }

    public override bool Equals(object obj)
    {
        // Если сравниваемые объекты не равны, или один из них = null
        if (obj == null || this.GetType() != obj.GetType())
            return false;

        B b = (B)obj;

        // Сравниваем поля текущего объекта и сравниваемого
        return (x == b.x) && (y == b.y);
    }

    // Если переопределяем Equals, надо переопределять и GetHashCode
    public override int GetHashCode()
    {
        return x * y;
    }
}

class A
{
    // Нет разницы между Equals(bo, b1) и b0.Equals(b1)
    static void Main()
    {
        B b0 = new B(1, 2);
        B b1 = new B(1, 2);
        B b2 = new B(0, 0);

        // Без переопределения в обоих было-бы false
        Console.WriteLine(b0.Equals(b1)); // true
        Console.WriteLine(b0.Equals(b2)); // false

        // Здесь сравниваются не хеш-коды, а адреса в памяти
        Console.WriteLine(object.ReferenceEquals(b0, b1)); // false

        // Здесь сравниваются хеш-коды
        Console.WriteLine(object.Equals(b0, b1)); // true
    }
}

Перегрузка + — * / ++ —:

// Слово operator можно использовать только со словом static
struct B
{
    private int x, y;

    public B(int X, int Y)
    {
        this.x = X;
        this.y = Y;
    }

    // Аналогично - * /
    public static B operator+(B b0, B b1)
        { return new B(b0.x + b1.x, b0.y + b1.y); }

    // Аналогично --
    // Одинаково для префиксной и постфиксной
    public static B operator++(B b0)
        { return new B(b0.x + 1, b0.y + 1); }

    public static bool operator==(B b0, B b1)
        { return b0.Equals(b1); }

    public static bool operator !=(B b0, B b1)
        { return !b0.Equals(b1); }

    public override string ToString()
        { return string.Format(this.x + " " + this.y); }

    // При перегрузке == != требуется переопределить Equals
    public override bool Equals(object obj)
    {
        if (((B)obj).x == this.x &&
            ((B)obj).y == this.y)
            return true;
        return false;
    }

    // При переопределении Equals требуется переопределить GetHashCode
    public override int GetHashCode()
        { return this.ToString().GetHashCode(); }
}

class Program
{
    static void Main()
    {
        B b0 = new B(1, 2);
        B b1 = new B(3, 4);
        B b = b0 + b1;
        Console.WriteLine(b);
    }
}

Перегрузка > >= < <=:

struct B: IComparable
{
    private int x, y;

    public B(int X, int Y)
    {
        this.x = X;
        this.y = Y;
    }

    public static bool operator<(B b0, B b1)
        { return (b0.CompareTo(b1) < 0); }

    public static bool operator >(B b0, B b1)
        { return (b0.CompareTo(b1) > 0); }

    public static bool operator >=(B b0, B b1)
        { return (b0.CompareTo(b1) >= 0); }

    public static bool operator <=(B b0, B b1)
        { return (b0.CompareTo(b1) <= 0); }

    public int CompareTo(object obj)
    {
        if (obj is B)
        {
            B b = (B)obj;

            if (this.x > b.x && this.y > b.y)
                { return 1; }
            else if (this.x < b.x && this.y < b.y)
                { return -1; }
            else
                return 0;
        }
        else
            throw new ArgumentException();
    }
}

class Program
{
    static void Main()
    {
        B b0 = new B(1, 2);
        B b1 = new B(3, 4);
        Console.WriteLine(b0 > b1);
    } 
}
Comments are closed.