Глава.9 .Параметры
StartProcessingFiles(out fs);
// Продолжаем, пока остаются файлы для обработки
for (; fs != null; ContinueProcessingFiles(ref fs)) {
// Обработка файла
fs.Read(...);
}
}
private static void StartProcessingFiles(out FileStream fs) {
fs = new FileStream(...); // в этом методе объект fs
// должен инициализироваться
}
private static void ContinueProcessingFiles(ref FileStream fs) {
fs.Close(); // Закрытие последнего обрабатываемого файла
// Открыть следующий файл или вернуть null, если файлов больше нет
if (noMoreFilesToProcess) fs = null;
else fs = new FileStream (...);
}
}
Как видите, главная особенность этого кода в том, что методы с параметрами
ссылочного типа, помеченными ключевыми словами
out
или
ref
, создают объект
и возвращают вызывающему коду указатель на него. Обратите внимание, что метод
ContinueProcessingFiles
может управлять передаваемым ему объектом, прежде
чем вернет новый объект. Это возможно благодаря тому, что его параметр помечен
ключевым словом
ref
. Показанный здесь код можно немного упростить:
using System;
using System.IO;
public sealed class Program {
public static void Main() {
FileStream fs = null; // Обязательное присвоение
// начального значения null
// Открытие первого файла для обработки
ProcessFiles(ref fs);
// Продолжаем, пока остаются необработанные файлы
for (; fs != null; ProcessFiles(ref fs)) {
// Обработка файла
fs.Read(...);
}
}
private static void ProcessFiles(ref FileStream fs) {
// Если предыдущий файл открыт, закрываем его
255
Передача.параметров.в.метод.по.ссылке
if (fs != null) fs.Close(); // Закрыть последний обрабатываемый файл
// Открыть следующий файл или вернуть null, если файлов больше нет
if (noMoreFilesToProcess) fs = null;
else fs = new FileStream (...);
}
}
Еще один пример, демонстрирующий использование ключевого слова
ref
для
реализации метода, меняющего местами два ссылочных типа:
public static void Swap(ref Object a, ref Object b) {
Object t = b;
b = a;
a = t;
}
Кажется, что код, меняющий местами ссылки на два объекта типа
String
, дол-
жен выглядеть так:
public static void SomeMethod() {
String s1 = "Jeffrey";
String s2 = "Richter";
Swap(ref s1, ref s2);
Console.WriteLine(s1); // Выводит "Richter"
Console.WriteLine(s2); // Выводит "Jeffrey"
}
Однако компилироваться этот код не будет: ведь переменные, передаваемые
методу по ссылке, должны быть одного типа, объявленного в сигнатуре метода.
Иначе говоря, метод
Swap
ожидает получить ссылки на тип
Object
, а не на тип
String
. Решение же нашей задачи выглядит следующим образом:
public static void SomeMethod() {
String s1 = "Jeffrey";
String s2 = "Richter";
// Тип передаваемых по ссылке переменных должен
// соответствовать ожидаемому
Object o1 = s1, o2 = s2;
Swap(ref o1, ref o2);
// Приведение объектов к строковому типу
s1 = (String) o1;
s2 = (String) o2;
Console.WriteLine(s1); // Выводит "Richter"
Console.WriteLine(s2); // Выводит "Jeffrey"
}
Такая версия метода
SomeMethod
будет компилироваться и работать нужным
нам образом. Причиной ограничения, которое нам пришлось обходить, является
256
Глава.9 .Параметры
обеспечение безопасности типов. Вот пример кода, нарушающего безопасность
типов (к счастью, он не компилируется):
internal sealed class SomeType {
public Int32 m_val;
}
public sealed class Program {
public static void Main() {
SomeType st;
// Следующая строка выдает ошибку CS1503: Argument '1':
// cannot convert from 'ref SomeType' to 'ref object'.
GetAnObject(out st);
Console.WriteLine(st.m_val);
}
private static void GetAnObject(out Object o) {
o = new String('X', 100);
}
}
Совершенно ясно, что здесь метод
Main
ожидает от метода
GetAnObject
объект
SomeType
. Однако поскольку в сигнатуре
GetAnObject
задана ссылка на
Object
,
метод
GetAnObject
может инициализировать параметр
o
объектом произвольного
типа. В этом примере параметр
st
в момент, когда метод
GetAnObject
возвращает
управление методу
Main
, ссылается на объект типа
String
, в то время как ожида-
ется тип
SomeType
. Соответственно, вызов метода
Console.WriteLine
закончится
неудачей. Впрочем, компилятор C# откажется компилировать этот код, так как
st
представляет собой ссылку на объект типа
SomeType
, тогда как метод
GetAnObject
требует ссылку на
Object
.
Однако, как оказалось, эти методы можно заставить работать при помощи обоб-
щений. Вот так следует исправить показанный ранее метод
Swap
:
public static void Swap(ref T a, ref T b) {
T t = b;
b = a;
a = t;
}
После этого следующий код (идентичный ранее показанному) будет без проблем
компилироваться и выполняться:
public static void SomeMethod() {
String s1 = "Jeffrey";
String s2 = "Richter";
Swap(ref s1, ref s2);
Console.WriteLine(s1); // Выводит "Richter"
Console.WriteLine(s2); // Выводит "Jeffrey"
}
257
Передача.переменного.количества.аргументов
За другими примерами решения, использующими обобщения, обращайтесь к
классу
System.Threading.Interlocked
с его методами
CompareExchange
и
Exchange
.
Do'stlaringiz bilan baham: |