Введення, C #, ASP, статті

Коли говорять про. NET мовами, насамперед мають на увазі C # – мова з’явився разом з першою версією. NET Framework і є основним мовою. NET. Чому це там можна вести тривалі суперечки і приводити безліч аргументів, але основним з них буде те, що мова спеціально створювався під нову платформу, тому з моменту свого народження він підтримував всі концепції платформи. NET.

У цій статті я розповім про нововведення в мові C #, які були привнесені в мову з виходом першої Beta версії. NET Framework 2. Основне завдання статті – розповісти розробникам про нові чудових можливостей, доступних при розробці програм на C #.

Зовнішні псевдоніми

Зовнішні псевдоніми (external aliases) дозволяють використовувати різні збірки з однаковими просторами імен як різні простору імен. Звучить заплутано, але насправді ідея проста, кожної окремої збірці призначається своє глобальне простір імен. Наприклад у нас є дві збірки Functions.dll і Globals.dll, кожна з яких містить простір імен PublicFunctions.

Збірка Functions.dll
namespace PublicFunctions
{
 public class Functionality
 { / / Функції складання Functions.dll
 }
}
 Збірка Globals.dll
namespace PublicFunctions
{
 public class Functionality
 { / / Функції складання Globals.dll
 }
}

Як бачите, при використанні двох цих збірок може виникнути конфлікт, тому ми призначаємо кожної з збірок “зовнішній простір імен “за яким і будемо звертатися до функцій тієї чи іншої збірки.

extern alias Functions;
extern alias Globals;
public class Main
{
  public Main()
  {
    Functions.PublicFunctions.Functionality.SomeMethod();
    Globals.PublicFunctions.Functionality.AnotherMethod();
  }
}

Специфікатори до псевдонімів

Як деякий доповнення і розширення попереднього параграфа статті потрібно сказати про нову можливість по роботі з псевдонімами для просторів імен. Наприклад, щоб звернутися до глобального простору імен можна використовувати синтаксис global :: namespace.

global::Microsoft.Win32;

А для того, щоб звернутися до псевдоніму простору імен, а не однойменним класу потрібно використовувати синтаксис
MyAlias::MySubNamespace.SomeClass.

using Win32 = Microsoft.Win32;
// ...
Win32::SystemEvents.CreateTimer(100);

Загальні типи (Generics)

Загальні (або параметризрвані) типи (generics) Дозволяють при описі класів, структур, методів і інтерфейсів використовувати параметризрвані параметри (не вказувати тип параметра в момент написання коду). Тип параметра визначається в момент оголошення змінної відповідного типу. Таким чином можна створити певний загальний елемент, тип який можна використовувати в подальшому для даних різних типів. Програмісти на C + + можуть вгледіти з загальних типах схожість з шаблонами (templates), У чомусь ця аналогія буде вірна, але тут існують деякі обмеження, які ми розглянемо трохи пізніше.

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

class Generics<TYPE1, TYPE2>
{
  private TYPE1 mVar1;
  private TYPE2 mVar2;
  public Generics(TYPE1 Var1, TYPE2 Var2)
  {
    this.mVar1 = Var1;
    this.mVar2 = Var2;
  }
  public string ToStringFunction(string Delemiter)
  {
    return this.mVar1.ToString() + Delemiter + this.mVar2.ToString();
  }
  public TYPE1 Variable1
  {
    get
    {
      return this.mVar1;
    }
    set
    {
      this.mVar1 = value;
    }
  }
  public TYPE2 Variable2
  {
    get
    {
      return this.mVar2;
    }
    set
    {
      this.mVar2 = value;
    }
  }
}

Як видно з прикладу, для того щоб використовувати загальні типи потрібно після оголошення класу вказати параметризрвані типи: Generics оголошує клас з двома параметризованих типами. Тепер використовуємо написаний клас:

/ / Оголошення
Generics<string, string> strGeneric = new Generics<string, string>("Hello","world");
Generics<int, int> intGeneric = new Generics<int,int>(1, 2);
Generics<string, int> strintGeneric = new Generics<string,int>("Three", 3);
int intSum;
string strSum;
 / / Використання
intSum = intGeneric.Variable1 + intGeneric.Variable2;
strSum = strintGeneric.Variable1 + " " + strintGeneric.Variable2.ToString();
MessageBox.Show("
strGeneric:
" + strGeneric.Variable1 + " " + strGeneric.Variable2 + 
"

intGeneric sum:
" + intSum.ToString() +
"

strintGeneric sum:
" + strSum.ToString());

Таким чином очевидно, що створений клас який можна використовувати з параметрами різних типів, які встановлюються в момент оголошення класу. Аналогічним чином можна оголосити структуру або інтерфейс:

public struct GenericStruct<TYPE>
{
  public TYPE someField;
}
public interface IGeneric<TYPE>
{
  TYPE SomeMethod();
  TYPE AnotherMethod();
}

Більш того, параметризрвані типи можуть бути використані при оголошенні делегатів функцій. Продемонструю цю можливість використовуючи оголошений вищий клас Generics.

/ / Оголошуємо делегат
public delegate DELEGATETYPE GenericDelegate<DELEGATETYPE, PARAMTYPE> (PARAMTYPE Param);
/ / Використовуємо делегат
Generics<string, string> strGeneric = new Generics<string, string>("Hello", "world");
GenericDelegate<string, string> genDelegate = 
			new GenericDelegate<string, string>(strGeneric.ToStringFunction); / / Виклик делагата
MessageBox.Show(genDelegate(" my "));

Переваги використання загальних типів

“Вау!” – Вигукнуть програмісти на C + + використовують у своїй роботі також і C #. “І що нам з того?” – Скажуть програмісти на C # ніколи не працювали з шаблонами С + +. Які ж переваги дає використання загальних типів?

  1. Найбільш очевидне – повторне використання коду. Немає необхідності створювати два ідентичних класу, що відрізняються тільки типами параметрів, досить створити один з параметризованих типами. При цьому використання параметризованих типів дозволяє створювати єдиний програмний код для роботи з різними типами даних. Наприклад, одного разу написаний алгоритм може працювати і з цілими числами і з числами з плаваючою точкою, при цьому не виробляючи на кожному кроці перевірку / приведення типу. Так Generics витісняють класи оголошені з використанням типу object.
  2. Підвищення продуктивності коду в порівнянні з використання параметрів типу object – немає необхідності виконувати приведення, як вже сказано вище, на кожному кроці, за рахунок чого виходить виграш в продуктивності.
  3. Перевірка типів у момент компіляції програми. Оскільки не використовуються параметри типу object, то компілятор може виконати перевірку типу кожного параметра в момент компіляції, оскільки типи для Generic класів жорстко задаються в момент оголошення змінних класів цього типу.

На жаль, досвідчених програмістів на C + + я повинен дещо розладнати. Загальні типи таки не відповідають шаблонам в C + +, оскільки параметризрвані типи в C # не можуть мати типів за замовчуванням. Параметризрвані типи не можуть бути використані в якості базових класів для загальних типів. Також в C # не допускається використання Generic класів в якості параметрів типів в інших Generic класах.

Але, незважаючи на це, загальні типи все-таки досить корисним нововведенням мови C #, особливо цінним і зручним для розробників використовують математичні алгоритми, оскільки переваги використання Generic класів очевидні.

Статичні класи

Статичні класи (static) – класи містять тільки статичні функції. Наприклад, певний клас дозволяє отримати доступ до налаштувань програми зберігається в реєстрі або в базі даних може бути оголошений як:

public static class AppSettings
{
  public static string BaseDir
  {
  }
  public static string GetRelativeDir
  {
  } / / І т.д. і т.п.
}

Примірник такого класу не може бути створений з використанням оператора new, оскільки всі члени цього класу статичні і доступні з використанням імені цього класу, наприклад AppSettings.BaseDir і т.п.

Поділ класів

Поділ класів (partial types) дозволяє розбивати код класу на кілька різних частин. Наприклад, ми маємо клас User:

public class User
{
  private int mInt;
  private long mLong;
  private string mString;
  private bool mBool;
  public int MyInt{
    get{return this.mInt;}
    set{this.mInt = value;}
  }
}

Ми можемо розбити його на декілька частин використовуючи ключове слово
partial:

public partial class User
{ / / Тут міститься генерований код
  private int mInt;
  private long mLong;
  private string mString;
  private bool mBool;
}
public partial class User
{ / / Тут міститься код, написаний вручну
  public int MyInt{
    get{return this.mInt;}
    set{this.mInt = value;}
  }
}

Для компілятора ці два способи опису класу рівнозначні. Тоді навіщо потрібна така можливість розділяти клас? Перш за все для відділення коду створюваного генератором коду і розробником, як наприклад це зроблено для підтримки дизайнера форма в Visual Studio 2005. Якщо ви звернете увагу, то помітите, що код розташовує елементи на формі за замовчуванням прихований і відділений від код написаного вами саме з використанням ключового слова partial.

Також подібна можливість може бути використана для розділення функцій класу по декільком файлам, що дозволить одночасно працювати з різними частинами класу декільком розробникам.

Ітератори

Загальновідомо, що для того щоб перебрати всі елементи в деякій колекції використовується метод foreach. Для тих, хто ніколи не створював власні колекції дозволяють перебирати елементи цей механізм був невідомий, тому варто описати процес створення колекції, підтримуючої послідовний перебір елементів за допомогою синтаксису
foreach.

Для того, щоб колекція підтримувала foreach необхідно реалізувати метод GetEnumerator, який повертає спеціальний клас за допомогою якого проводиться визначення порядку виведення елементів. Для прикладу створимо колекцію, при проході за допомогою foreach якої, елементи повертаються в порядку їх розташування в масиві.

public class MyUsualCollection
{
 public int[] myItems;
 public MyUsualCollection()
 {
   myItems = new int[10] { 1,2,3,4,5,6,7,8,9,10 };
 }
 public MyUsualEnumerator GetEnumerator()
 {
   return new MyUsualEnumerator(this);
 }
}
 / / Клас Enumerator для нашої колекції
public class MyUsualEnumerator
{
 int indexEnum;
 MyUsualCollection myCol;
 public MyUsualEnumerator(MyUsualCollection col)
 {
   this.myCol = col;
   indexEnum = -1;
 }
 public bool MoveNext()
 { indexEnum + +; / / переміщаємося далі
   return (indexEnum < this.myCol.myItems.GetLength(0));
 }
 public int Current
 {
   get
   {
     return (this.myCol.myItems[indexEnum]);
   }
 }
}

Очевидно, що для такої простої операції занадто багато коду. Тому-то в C # з виходом Visual Studio 2005 і Framework 2 з’явився простіший шлях підтримки перебору елементів. Того ж результату ми доб’ємося написавши наступний код:

public class MyIteratorCollection
{
  public int[] myItems;
  public MyIteratorCollection()
  {
    myItems = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  }
  public IEnumerator GetEnumerator()
  {
    for (int i = 0; i < 10; i++)
      yield return myItems[i];
  }
}

Погодьтеся, коли компілятор бере вашу роботу на себе це приємно!

Безіменні методи

Безіменні методи (anonymous methods) дозволяють значно скоротити обсяг коду, який повинен написати розробник. Найбільш просте і зрозуміле використання безіменних методів при призначенні щодо простих обробників подій. Розглянемо приклад, нехай у нас є форма, на якій розміщені текстове поле txtLogin і кнопка btnLoginі нам потрібно, щоб при зміні тексту в текстовому полі, змінювався текст кнопки. Зрозуміло, що для цього необхідно в обробнику події TextChanged змінювати текст кнопки.

Який код створює Visual Studio при додаванні нового обробника події? Перш за все функцію обробник і запис про відповідність функції обробника події, що здійснюється присвоєнням відповідного делегата відповідного події контрола TextBoxв функції
InitializeComponent:

this.txtLogin.TextChanged += new System.EventHandler(this.txtLogin_TextChanged);

Сам обробник виглядає так:

private void txtLogin_TextChanged(object sender, EventArgs e)
{
  btnLogin.Text = "Login [" + txtLogin.Text + "]";
} 

Ті ж операції можна виконати вручну створивши, наприклад, такий код:

public frmMain()
{
  InitializeComponent();
  this.txtLogin.TextChanged += new System.EventHandler(this.txtLogin_TextChanged);
}
private void txtLogin_TextChanged(object sender, EventArgs e)
{
  btnLogin.Text = "Login [" + txtLogin.Text + "]";
} 

Тепер же перепишемо цей код з використанням безіменних методів:

public frmMain()
{
  InitializeComponent();
  this.txtLogin.TextChanged += delegate { btnLogin.Text = "Login [" + txtLogin.Text + "]"; };
}

Таким чином ми помістили код обробника безпосередньо в місце присвоювання делегата події TextChanged. Формально цей процес нічого не змінює – в момент компіляції компілятор сам створить функцію і присвоїть їй деякий ім’я, а потім створить делегат для цієї функції і присвоїть його події TextChanged. Але відзначте, на скільки менше рядків коду потрібно для реалізації, при цьому жодних втрат продуктивності після компіляції не буде, оскільки в результаті буде отриманий такий же код, що і в прикладах описаних вище.

Звичайно, наведений приклад практичної цінності не має (а якщо і має, але досить і досить невелику), але демонструє загальний шлях використання безіменних методів. При цьому ніщо не заважає отримати доступ до переданих функції параметрам, для цього потрібно лише явно вказати їх при написанні безіменного методу:

this.btnLogin.Click += delegate(object sender, EventArgs e) 
{MessageBox.Show(((Button)sender).Text.ToString());};

На додаток приведу більш загальний приклад використання безіменних методів:

public delegate int MyDelegate(int mInt1, int mInt2);
private void btnAnonymous_Click(object sender, EventArgs e)
{
  MyDelegate anSum = delegate(int a, int b) { return a + b; };
  MessageBox.Show(anSum(1, 2).ToString());
}

У прикладі новий делегат створюється безпосередньо в обробнику події клацання по кнопці.

Нововведення в поведінці властивостей

В C # з виходом. NET Framework 2 і Visual Studio 2005возможно обмежувати видимість get і set блоків властивостей класів, наприклад:

public partial class User
{
  public int MyInt{
    get{return this.mInt;}
    private set{this.mInt = value;}
  }
}

Таким чином тільки всередині класу можна буде встановити властивість MyInt, “ззовні” така можливість буде недоступна. Такий підхід спрощує код доступу до властивостей, більше немає необхідності звертатися до внутрішньої змінної, досить зробити блок set недоступним для “Зовнішнього впливу” і звертатися до цієї змінної використовуючи властивість.

Висновок

У цій статті я розповів про деякі нововведення в мові C #. Звичайно, ця стаття не претендує на повноту викладу та опис перелічених новинок. Мета статті – дати інформацію про нові можливості доступних розробнику. За детальним описом я раджу звернутися до відповідним монографій присвяченим C # і MSDN.

Я з радістю прийму пропозиції та коментарі читачів! Пишіть
gaidar at vbstreets.ru і я постараюся відповісти кожному.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*