Методы расширения (extension methods) позволяют добавлять новые методы в уже существующие типы без создания нового производного класса. Эта функциональность бывает особенно полезна, когда нам хочется добавить в некоторый тип новый метод, но сам тип (класс или структуру) мы изменить не можем.
Например, нам надо добавить для типа string новый метод:
class Program
{
static void Main(string[] args)
{
string s = "Привет мир";
char c = 'и';
int i = s.WordCount(c);
Console.WriteLine(i);
Console.ReadLine();
}
}
public static class StringExtension
{
public static int WordCount(this string str, char c)
{
int counter = 0;
for (int i = 0; i<str.Length; i++)
{
if (str[i] == c)
counter++;
}
return counter;
}
}
Для того, чтобы создать метод расширения, вначале надо создать статический класс, который и будет содержать этот метод. В данном случае это класс StringExtension. Затем объявляем статический метод. Суть нашего метода расширения - подсчет количества определенных символов в строке.
Собственно метод расширения - это обычный статический метод, который в качестве первого параметра всегда принимает такую конструкцию: this имя_типа название_параметра, то есть в нашем случае this string str. Так как наш метод будет относиться к типу string, то мы и используем данный тип.
|
|
Затем у всех строк мы можем вызвать данный метод: int i = s.WordCount(c);. Причем нам уже не надо указывать первый параметр. Значения для остальных параметров передаются в обычном порядке.
Применение методов расширения очень удобно, но при этом надо помнить, что метод расширения никогда не будет вызван, если он имеет ту же сигнатуру, что и метод, изначально определенный в типе.
Также следует учитывать, что методы расширения действуют на уровне пространства имен. То есть, если добавить в проект другое пространство имен, то метод не будет применяться к строкам, и в этом случае надо будет подключить пространство имен метода через директиву using.
Лекция 17. Локальные функции
Локальные функции представляют функции, определенные внутри других методов. Локальные функции доступны начиная с версии C# 7.0 (Visual Studio 2017).
Определим и используем локальную функцию:
class Program
{
static void Main(string[] args)
{
var result = GetResult(new int[] { -3, -2, -1, 0, 1, 2, 3 });
Console.WriteLine(result); // 6
Console.Read();
}
static int GetResult(int[] numbers)
{
int limit = 0;
// локальная функция
bool IsMoreThan(int number)
{
return number > limit;
}
int result = 0;
for(int i=0; i < numbers.Length; i++)
{
if (IsMoreThan(numbers[i]))
{
result += numbers[i];
}
}
return result;
}
}
Здесь в методе GetResult определена локальная функция IsMoreThan(), которая может быть вызвана только внутри этого метода. Локальная функция задает еще одну область видимости, где мы можем определять переменные и выполнять над ними действия. В то же время ей доступны все переменные, которые определены в том же методе.
|
|
При использовании локальных функций следует помнить, что они не могут иметь модификаторов доступа (public, private, protected). Нельзя определить в одном методе несколько локальных функций с одним и тем же именем, даже если у них будет разный список параметров. Кроме того, не имеет значения, определены локальные функции до своего вызова или после.
Лекция 18. Пространства имен, псевдонимы и статический импорт
Пространства имен
Все определяемые классы не существуют сами по себе, а, как правило, заключаются в специальные контейнеры - пространства имен. Создаваемый по умолчанию класс Program уже находится в пространстве имен, которое обычно совпадает с названием проекта:
namespace HelloApp
{
class Program
{
static void Main(string[] args)
{
}
}
}
Пространство имен определяется с помощью ключевого слова namespace, после которого идет название. Так в данном случае полное название класса Program будет HelloApp.Program.
Класс Program видит все классы, которые объявлены в том же пространстве имен:
namespace HelloApp
{
class Program
{
static void Main(string[] args)
{
Account account = new Account(4);
}
}
class Account
{
public int Id { get; private set;} // номер счета
public Account(int _id)
{
Id = _id;
}
}
}
Но чтобы задействовать классы из других пространств имен, эти пространства надо подключить с помощью директивы using:
using System;
namespace HelloApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("hello");
}
}
}
Здесь подключается пространство имен System, в котором определен класс Console. Иначе нам бы пришлось писать полный путь к классу:
static void Main(string[] args)
{
System.Console.WriteLine("hello");
}
Пространства имен могут быть определены внутри других пространств:
using HelloApp.AccountSpace;
namespace HelloApp
{
class Program
{
static void Main(string[] args)
{
Account account = new Account(4);
}
}
namespace AccountSpace
{
class Account
{
public int Id { get; private set;}
public Account(int _id)
{
Id = _id;
}
}
}
}
В этом случае для подключения пространства указывается его полный путь с учетом внешних пространств имен: using HelloApp.AccountSpace;
Псевдонимы
Для различных классов мы можем использовать псевдонимы. Затем в программе вместо названия класса используется его псевдоним. Например, для вывода строки на экран применяется метод Console.WriteLine(). Но теперь зададим для класса Console псевдоним:
using printer = System.Console;
class Program
{
static void Main(string[] args)
{
printer.WriteLine("Hello from C#");
printer.Read();
}
}
С помощью выражения using printer = System.Console указываем, что псевдонимом для класса System.Console будет имя printer. Это выражение не имеет ничего общего с подключением пространств имен в начале файла, хотя и использует оператор using. При этом используется полное имя класса с учетом пространства имен, в котором класс определен. И далее, чтобы вывести строку, применяется выражение printer.WriteLine("Hello from C#").
И еще пример. Определим класс и для него псевдоним:
using Person = HelloApp.User;
using Printer = System.Console;
namespace HelloApp
{
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.name = "Tom";
Printer.WriteLine(person.name);
Printer.Read();
}
}
class User
{
public string name;
}
}
Класс называется User, но в программе для него используется псевдоним Person.
Начиная с версии C# 6.0 (Visual Studio 2015) в язык была добавлена возможность импорта функциональности классов. Например, опять же возьмем класс Console и применим новую функциональность:
using static System.Console;
|
|
namespace HelloApp
{
class Program
{
static void Main(string[] args)
{
WriteLine("Hello from C# 6.0");
Read();
}
}
}
Выражение using static подключает в программу все статические методы и свойства, а также константы. И после этого мы можем не указывать название класса при вызове метода.
Подобным образом можно определять свои классы и импортировать их:
using static System.Console;
using static System.Math;
using static HelloApp.Geometry;
namespace HelloApp
{
class Program
{
static void Main(string[] args)
{
double radius = 50;
double result = GetArea(radius); //Geometry.GetArea
WriteLine(result); //Console.WriteLine
Read(); // Console.Read
}
}
class Geometry
{
public static double GetArea(double radius)
{
return PI * radius * radius; // Math.PI } } }