Static возращаемый_тип имя (this тип_вызывающего_объекта ob, список_параметров)



 

Очевидно, что список_параметров окажется пустым в отсутствие аргументов, за исключением аргумента, неявно передаваемого вызывающим объектом оb. Не следует, однако, забывать, что первым параметром метода расширения является автоматически передаваемый объект, для которого вызывается этот метод. Как правило, метод расширения становится открытым членом своего класса.

В приведенном ниже примере программы создаются три простых метода расширения.

 

// Создать и использовать ряд методов расширения,

using System;

using System.Globalization;

static class MyExtMeths {

Возвратить обратную величину числового значения типа double,

public static double Reciprocal(this double v) {

return 1.0 / v;

}

 

Изменить на обратный регистр букв в символьной

Строке и возвратить результат,

public static string RevCase(this string str) {

string temp = "";

 

foreach(char ch in str) {

if(Char.IsLower(ch))  

temp += Char.ToUpper(ch, CultureInfo.

CurrentCulture);

else temp += Char.ToLower(ch, CultureInfo.CurrentCulture);

}

return temp;

}

 

Возвратить абсолютное значение выражения n/d.

public static double AbsDivideBy(this double n, double d) {

return Math.Abs(n / d);

}

}

 

class ExtDemo {

static void Main() {

double val = 8.0;

string str = "Alpha Beta Gamma";

 

Вызвать метод расширения Reciprocal()..

Console.WriteLine("Обратная величина {0} равна {1}",

val, val.Reciprocal());

 

Вызвать метод расширения RevCaseO .

Console.WriteLine(str + " после смены регистра: " +

str.RevCase());

 

Использовать метод расширения AbsDivideBy() .

Console.WriteLine("Результат вызова метода val.AbsDivideBy(-2) : "

+ val.AbsDivideBy(-2));

}

}

 

Эта программа дает следующий результат.

 

Обратная величина 8 равна 0.125

Alpha Beta Gamma после смены регистра: aLPHA bЕТА gАММА

Результат вызова метода val.AbsDivideBy(-2): 4

 

В данном примере программы каждый метод расширения содержится в статическом классе MyExtMeths. Как пояснялось выше, метод расширения должен быть объявлен в статическом классе. Более того, этот класс должен находиться в области действия своих методов расширения, чтобы ими можно было пользоваться. (Именно поэтому в исходный текст программы следует включить пространство имен System.Linq, так как это дает возможность пользоваться методами расширения, связанными с LINQ.)

Объявленные методы расширения вызываются для объекта таким же образом, как и методы экземпляра. Главное отличие заключается в том, что вызывающий объект передается первому параметру метода расширения. Поэтому при выполнении выражения

 

Val.AbsDivideBy(-2)

 

объект val передается параметру n метода расширения AbsDivideBy(), а значение -2 — параметру d.

Любопытно, что методы расширения Reciprocal() и AbsDivideBy() могут вполне законно вызываться и для литерала типа double, как показало ниже, поскольку они определены для этого типа данных.

 

Reciprocal()

AbsDivideBy(-1)

 

Кроме того, метод расширения RevCase() может быть вызван следующим образом.

 

"AbCDe".RevCase()

 

В данном случае возвращается строковый литерал с измененным на обратный регистром букв.

 

 

PLINQ

 

В версии .NET Framework 4.0 внедрено новое дополнение LINQ под названием PLINQ. Это средство предназначено для поддержки параллельного программирования. Оно позволяет автоматически задействовать в запросе несколько доступных процессоров. Подробнее о PLINQ и других средствах, связанных с параллельным программированием, речь пойдет в главе 24.

 

 

ГЛАВА 20 Небезопасный код, указатели, обнуляемые типы и разные ключевые слова

 

В этой главе рассматривается средство языка С#, которое обычно захватывает программистов врасплох. Это небезопасный код. В таком коде зачастую используются указатели. Совместно с небезопасным кодом указатели позволяют разрабатывать на C# приложения, которые обычно связываются с языком C++, высокой производительностью и системным кодом. Более того, благодаря включению небезопасного кода и указателей в состав C# в этом языке появились возможности, которые отсутствуют в Java.

В этой главе рассматриваются также обнуляемые типы, определения частичных классов и методов, буферы фиксированного размера. И в заключение этой главы представлен ряд ключевых слов, не упоминавшихся в предыдущих главах.

 

 

Небезопасный код

 

В C# разрешается писать так называемый "небезопасный" код. В этом странном на первый взгляд утверждении нет на самом деле ничего необычного. Небезопасным считается не плохо написанный код, а такой код, который не может быть выполнен под полным управлением в общеязыковой исполняющей среде (CLR). Как пояснялось в главе 1, результатом программирования на C# обычно является управляемый код. Тем не менее этот язык программирования допускает написание кода, который не выполняется под полным управлением в среде CLR. Такой неуправляемый код не подчиняется тем же самым средствам управления и ограничениям, что и управляемый код, и называется он небезопасным потому, что нельзя никак проверить, не выполняет ли он какое-нибудь опасное действие. Следовательно, термин небезопасный совсем не означает, что коду присущи какие-то изъяны. Это просто означает, что код может выполнять действия, которые не подлежат контролю в управляемой среде.

Если небезопасный код может вызвать осложнения, то зачем вообще создавать такой код? Дело в том, что управляемый код не позволяет использовать указатели. Если у вас имеется некоторый опыт программирования на С или C++, то вам должно быть известно, что указатели представляют собой переменные, предназначенные для хранения адресов других объектов, т.е. они в какой-то степени похожи на ссылки в С#. Главное отличие указателя заключается в том, что он может указывать на любую область памяти, тогда как ссылка всегда указывает на объект своего типа. Но поскольку указатель способен указывать практически на любую область памяти, то существует большая вероятность его неправильного использования. Кроме того, используя указатели, легко допустить программные ошибки. Именно поэтому указатели не поддерживаются при создании управляемого кода в С#. А поскольку указатели все-таки полезны и необходимы для некоторых видов программирования (например, утилит системного уровня), в C# разрешается создавать и использовать их. Но при этом все операции с указателями должны быть помечены как небезопасные, потому что они выполняются вне управляемой среды.

В языке C# указатели объявляются и используются таким же образом, как и в С/С++. Если вы знаете, как пользоваться ими в С/С++, то вам нетрудно будет сделать это и в С#. Но не забывайте, что главное назначение C# — создание управляемого кода. А способность этого языка программирования поддерживать неуправляемый код следует использовать для решения лишь особого рода задач. Это, скорее, исключение, чем правило для программирования на С#. По существу, для компилирования неуправляемого кода следует использовать параметр компилятора /unsafe.

Указатели составляют основу небезопасного кода, поэтому мы начнем его рассмотрение именно с них.

 

 

Основы применения указателей

 

Указатель представляет собой переменную, хранящую адрес какого-нибудь другого объекта, например другой переменной. Так, если в переменной х хранится адрес переменной у, то говорят, что переменная х указывает на переменную у. Когда указатель указывает на переменную, то значение этой переменной может быть получено или изменено по указателю. Такие операции с указателями называют непрямой адресацией.

 

 

Объявление указателя

 

Переменные-указатели должны быть объявлены как таковые. Ниже приведена общая форма объявления переменной-указателя:

 

тип* имя_переменной;

 

где тип обозначает соотносимый тип, который не должен быть ссылочным. Это означает, что в C# нельзя объявить указатель на объект определенного класса. Соотносимый тип указателя иногда еще называют базовым. Обратите внимание на положение знака * в объявлении указателя. Он должен следовать после наименования типа. А имя_переменной обозначает конкретное имя указателя-переменной.

Обратимся к конкретному примеру. Для того чтобы сделать переменную ip указателем на значение типа int, необходимо объявить ее следующим образом.

 

int* ip;

 

А указатель типа float объявляется так, как показано ниже.

 

float* fp;

 

Вообще говоря, если в операторе объявления после имени типа следует знак *, то это означает, что создается переменная типа указателя.

Тип данных, на которые будет указывать сам указатель, зависит от его соотносимого типа. Поэтому в приведенных выше примерах переменная ip может служить для указания на значение типа int, а переменная fp — для указания на значение типа float. Следует, однако, иметь в виду, что указателю ничто не мешает указывать на что угодно. Именно поэтому указатели потенциально небезопасны.

Если у вас есть опыт программирования на C/C++, то вы должны ясно понимать главное отличие в объявлении указателей в C# и C/C++. При объявлении указателя в C/C++ знак * не разделяет список переменных в объявлении. Поэтому в следующей строке кода:

 

int* р, q;

 

объявляется указатель р типа int и переменная q типа int. Это равнозначно двум следующим объявлениям.

 

int* р;

int q;

 

А в C# знак * является разделительным, и поэтому в объявлении

 

int* р, q;

 

создаются две переменные-указателя. Это равнозначно двум следующим объявлениям.

 

int* р;

int* q;

 

Это главное отличие следует иметь в виду при переносе кода C/C++ на С#.

 

 

Операторы * и & в указателях

 

В указателях применяются два оператора: * и &. Оператор & является унарным и возвращает адрес памяти своего операнда. (Напомним, что для унарного оператора требуется единственный операнд.) Например, в следующем фрагменте кода:

 

int* ip;

int num = 10;

ip = #

 

в переменной ip сохраняется адрес памяти переменной num. Это адрес расположения переменной num в оперативной памяти компьютера. Он не имеет никакого отношения к значению переменной num. Поэтому в переменной ip содержится не значение 10, являющееся исходным для переменной num, а конкретный адрес, по которому эта переменная хранится в оперативной памяти. Операцию & можно рассматривать как возврат адреса той переменной, перед которой она указывается. Таким образом, приведенное выше присваивание словами можно описать так: "Переменная ip получает адрес переменной num."

Второй оператор, *, является дополнением оператора &. Этот унарный оператор находит значение переменной, расположенной по адресу, на который указывает его операнд. Следовательно, этот оператор обращается к значению переменной, на которую указывает соответствующий указатель. Так, если переменная ip содержит адрес памяти переменной num, как показано в предыдущем примере, то в следующей строке кода:

 

int val = *ip;

 

в переменной val сохраняется значение 10 переменной num, на которую указывает переменная ip. Операцию * можно рассматривать как получение значения по адресу. Поэтому приведенный выше оператор присваивания описывается словами следующим образом: "Переменная val получает значение по адресу, хранящемуся в переменной ip."

Оператор * можно использовать также в левой части оператора присваивания. В этом случае он задает значение, на которое указывает соответствующий указатель, как в приведенном ниже примере.

 

*ip = 100;

 

В данном примере значение 100 присваивается переменной, на которую указывает переменная ip, т.е. переменной num. Поэтому приведенный выше оператор присваивания описывается словами следующим образом: "Разместить значение 100 по адресу, хранящемуся в переменной ip."

 

 


Дата добавления: 2019-02-12; просмотров: 287; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!