Делегаты
Язык программирования C# хотя и допускает, но все же не поощряет использование указателей. В некоторых ситуациях бывает особенно трудно обойтись без указателей на функции. Для этих целей в C# реализованы так называемые делегаты (delegates), которые иногда еще называют безопасными аналогами указателей на функцию. Ниже приведен простейший пример использования метода-делегата:
using System;
delegate void MyDelegate();
class Example_5
{
static void Func()
{
System.Console.WriteLine(«MyDelegate.Func()»);
}
public static void Main()
{
MyDelegate f = new MyDelegate(Func);
f();
}
}
Помимо того что делегаты обеспечивают типовую защищенность, а следовательно, и повышают безопасность кода, они отличаются от обычных указателей на функции еще и тем, что являются объектами, производными от базового типа System.Delegate. Таким образом, если мы используем делегат для указания на статический метод класса, то он просто связывается с соответствующим методом данного класса. Если же делегат указывает на нестатический метод класса, он связывается уже с методом экземпляра такого класса. Это позволяет избежать нарушения принципов ООП, поскольку методы не могут быть использованы отдельно от класса (объекта), в котором они определены.
Еще одним отличием делегатов от простых указателей на функции является возможность вызова нескольких методов с помощью одного делегата. Рассмотримэтонаконкретномпримере:
using System;
delegate void MyDelegate(string message);
class Example_6
{
public static void Func1(string message)
{
Console.WriteLine(”{0}: MyDelegate.Func1”, message);
}
public static void Func2(string message)
{
Console.WriteLine(”{0}: MyDelegate.Func2”, message);
}
public static void Main()
{
MyDelegate f1, f2, f3;
f1 = new MyDelegate(Func1);
f2 = new MyDelegate(Func2);
f3 = f1 + f2;
f1(”Calling delegate f1”);
f2(”Calling delegate f2”);
f3(”Calling delegate f3”);
}
}
Откомпилировав и выполнив вышеприведенную программу, получим следующий результат:
Calling delegate f1: MyDelegate.Func1
Calling delegate f2: MyDelegate.Func2
Calling delegate f3: MyDelegate.Func1
Calling delegate f3: MyDelegate.Func2
Из этого следует, что вызов метода-делегата f3, полученного с помощью операции сложения f1 + f2, приводит к последовательному выполнению обоих этих методов. Подобно применению операции сложения с целью объединения делегатов, можно использовать и операцию вычитания, которая, как нетрудно догадаться, выполняет обратное действие.
Способы передачи параметров
Анализируя особенности реализации классов языка C#, хотелось бы уделить внимание и способам передачи параметров метода по ссылке. Иногда возникает потребность в том, чтобы функция возвращала сразу несколько значений. Рассмотрим это на примере программы, вычисляющей квадратный корень:
using System;
class Example_7
{
static int GetRoots(double a, double b, double c,
out double x1, out double x2)
{
double d = b * b - 4 * a * c;
if (d > 0)
{
x1 = -(b + Math.Sqrt(d)) / (2 * a);
x2 = -(b - Math.Sqrt(d)) / (2 * a);
return 2;
} else
if (d == 0)
{
x1 = x2 = -b / (2 * a);
return 1;
} else
{
x1 = x2 = 0;
return 0;
}
}
public static void Main()
{
double x1, x2;
int roots = GetRoots(3, -2, -5, out x1, out x2);
Console.WriteLine(”roots #: {0}”, roots);
if (roots == 2)
Console.WriteLine(”x1 = {0}, x2 = {1}”, x1, x2);
else
if (roots == 1)
Console.WriteLine(”x = {0}”, x1);
}
}
Чтобы функция GetRoots возвращала оба корня уравнения (x1 и x2), мы указали транслятору, что переменные x1 и x2 должны быть переданы по ссылке, применив для этого параметр out. Обратите внимание на то, что нам не обязательно инициализировать переменные x1 и x2 перед вызовом функции GetRoots. Обозначив функцию ключевым словом out, мы добьемся того, что ее аргументы могут использоваться только для возврата какого-то значения, но не для его передачи внутрь функции. Таким образом, подразумевается, что переменная будет инициализирована в теле самой функции. В случае же, если нам по какой-то причине потребуется передать в параметре функции некоторое значение с возможностью его последующего изменения, можно воспользоваться параметром ref. Действие этого параметра очень похоже на действие out, но он позволяет еще и передавать значение параметра телу функции. Второе отличие ключевого слова ref состоит в том, что передаваемый параметр функции должен быть инициализирован предварительно.
Такой метод очень напоминает использование параметра var в списке аргументов функций, принятое в языке программирования Паскаль, и является еще одним отличием от языка Java, где параметры всегда передаются по значению.
Заключение
Язык программирования C#, как и платформа .NET, находится в развитии. В частности, в ближайшее время можно ожидать появления обобщенных шаблонов, которые подобно шаблонам языка Cи++ позволят создавать сильно типизированные классы-коллекции. В любом случае язык программирования C# уже вполне сформировался для того, чтобы его изучить и начать применять в реальных приложениях.
Списоклитературы
C# Language Specification. MicrosoftCorporation, 2000.
Гуннерсон Э. Введение в C#. СПб.: Питер, 2001.
Бесплатная версия .NET Framework SDK Beta 1: www.microsoft.com/downloads.
Обширнейшая информация по платформе .NET: www.gotdotnet.com.
Официальная конференция по языку C#: news://msnews.microsoft.com/ microsoft.public.dotnet.languages.csharp.
Инструментарий С#
Прежде чем начать работу с языком программирования C#, необходимо установить на компьютере набор инструментальных средств под названием .Net Framework SDK, бета-версия которого доступна для бесплатной загрузки непосредственно c Web-страницы корпорации Microsoft [3]. Кроме того, понадобится хороший текстовый редактор, поддерживающий синтаксически настраиваемый ориентированный режим (syntax highlight) и позволяющий выделять ключевые слова в исходных текстах того или иного языка программирования. Я рекомендую программу SharpDevelop (www.icsharpcode.net), распространяемую независимыми программистами на условиях лицензии GNU. В крайнем случае можно использовать любой редактор, способный работать с исходными текстами на языке Cи/Cи++, или даже обычный текстовый редактор Notepad.
Основные отличия типов struct и class | ||
Тип class | Тип struct | |
Представление экземпляра типа | указатель | значение |
Местоположение объекта | куча | стек |
Значение по умолчанию | null | заполняется нулями |
Результат операции присваивания для экземпляров типа | копируется указатель | копируется сам объект |
Базовый тип | встроенный тип string | встроенный тип int |
C# и Java
Язык программирования C# часто и небезосновательно сравнивают с Java. Оба языка были созданы для аналогичных целей и имеют много общего, в том числе синтаксис, базирующийся на Cи++. В то же время есть и множество различий, относящихся к базовым типам, классам, способам передачи параметров, реализации интерфейсов и т. д. Основным же несходством между C# и Java является то, что Java-приложения работают со средой Java Frameworks and Runtime, а C#-приложения — со средой .NET Framework and Runtime. В полном объеме концепция .NET будет реализована только в новой операционной системе Windows XP (также известна как Whistler), хотя она уже около года активно продвигается корпорацией Microsoft. Похоже, если вы планируете создавать приложения, совместимые с платформой Microsoft, явно стоит поближе познакомиться с Microsoft .NET. Лучшим же языком для создания .NET-приложений, по утверждению самой корпорации Microsoft, является C#.
От двух до...
Исходный текст любого исполняемого приложения, написанного на языке программирования C#, содержит статический метод Main(), — аналог знакомой программистам Си/Си++ функции main(). Именно с этого метода начинается выполнение программы.
Что же произойдет, если исходный текст будет содержать два или более методов Main(), как показано ниже?
using System;
class SayHello
{
public static void Main()
{
Console.WriteLine(”Hello friend!”);
}
}
class SayBye
{
public static void Main()
{
Console.WriteLine(”Bye, bye...”);
}
}
Разумеется, компиляция этого примера вызовет сообщение об ошибке, так как классы SayHello и SayBye абсолютно «равноправны» с точки зрения транслятора. Процесс компиляции будет прерван. Однако существует специальный ключ компилятора /main, с помощью которого можно указать класс, содержащий нужный нам метод Main(). Вышеприведенный пример, откомпилированный с ключом /main:SayHello, напечатает сообщение:
Hello friend!
Если же откомпилировать тот же самый пример, указав ключ /main:SayBye, то текст будет иным:
Bye, bye...