Читайте также:
|
|
Обобщенный класс определяется так же, как и нормальный, но с объявлением обобщенного типа. Обобщенный тип затем может быть использован внутри класса как тип поля-члена или как тип параметров метода:
public class MyGeneric<T>
{
private T member;
public void Method(T obj)
{ }
}
Каждый класс, имеющий дело с объектным типом, является кандидатом на обобщенную реализацию. К тому же, если классы используют иерархии, то обобщения могут помочь исключить необходимость в приведении типов.
Ниже мы переделаем класс DocumentManager, который был определен ранее. Попытаемся построить класс, достаточно гибкий, чтобы он поддерживал не только тип Document. Вот новая версия класса:
public class DocumentManager<T>: IDocumentManager<T>
{
private readonly Queue<T> documentQueue = new Queue<T>();
public void AddDocument(T doc)
{
lock (this)
{
documentQueue.Enqueue(doc);
}
}
public T GetDocument()
{
T doc = default(T);
lock (this)
{
doc = documentQueue.Dequeue();
}
return doc;
}
public bool IsDocumentAvailable
{
get
{
return (documentQueue.Count > 0)? true: false;
}
}
}
Ранее класс Document использовался классом DocumentManager. Теперь мы объявили его как использующий тип Т. DocumentManager стал настолько гибким, что теперь позволяет использовать в качестве Т любой тип. Этот тип может применяться с переменными внутри класса. Как здесь показано, обобщенный тип также может быть применен при использовании обобщенного типа, такого как Queue<T>. Тот же тип, что служит параметром при создании экземпляра DocumentManager<T>, применяется для создания экземпляра Queue<T>:
public class DocumentManager<T>: IDocumentManager<T>
{
private readonly Queue<T> documentQueue = new Queue<T>();
Методы AddDocument() и GetDocument() определяют Т в качестве типа параметра и типа возврата:
public void AddDocument(T doc)
{
//....
}
public T GetDocument()
{
//.
}
Обобщенному типу невозможно присвоить null. Причина в том, что вместо него может быть подставлен тип значения, не допускающий null, a null допускаются только со ссылочными типами. Чтобы обойти эту проблему, можно воспользоваться ключевым словом default. Оно позволяет присваивать null ссылочным типам и 0 — типам значений.
T doc = default(T);
Проведем изменение класса ProcessDocuments для того чтобы сделать его обобщенным. Поскольку ProcessDocuments вызывает методы класса Document, для него должна использоваться конструкция where, специфицирующая методы и свойства, которые должны присутствовать у обобщенного типа. Конструкция where может требовать от обобщенного типа реализации интерфейса или наследования от определенного базового класса.
Новая версия ProcessDocuments будет независимой от класса Document, равно как и от класса DocumentManager. Это достигается определением интерфейсов IDocument и IDocumentManager<T>, которые определяют методы и свойства, используемые ProcessDocuments<T, U>.
Интерфейс IDocument определяет свойства Title и Content, доступные только для чтения:
public interface IDocument
{
string Title
{
get;
}
string Content
{
get;
}
}
Ранее созданный нами класс Document теперь реализует интерфейс IDocument:
public class Document: IDocument
{ }
IDocumentManager<T> — это обобщенный интерфейс. Тип Т для методов этого интерфейса от случая к случаю может быть реализован по-разному:
public interface IDocumentManager<T>
{
void AddDocument(T doc);
T GetDocument();
bool IsDocumentAvailable
{
get;
}
}
Теперь класс DocumentManager<T> реализует интерфейс
IDocumentManager<T>. Обобщенный тип для интерфейса определен классом DocumentManager<T>:
public class DocumentManager<T>: IDocumentManager<T>
{ }
Теперь изменим класс ProcessDocuments так, чтобы он использовал два обобщенных типа: TDocument и TDocumentManager. Первая конструкция where утверждает, что тип TDocument должен реализовывать интерфейс
IDocument. Одним из типов, который может быть подставлен вместо
TDocument, является Document, потому что этот класс реализует интерфейс IDocument. Однако и любой другой класс, реализующий интерфейс IDocument, также может быть подставлен вместо TDocument. Конструкция where для типа TDocumentManager определяет, что этот тип должен реализовывать интерфейс IDocumentManager<TDocument>. Поэтому в случае подстановки Document вместо TDocument TDocumentManager должен быть классом, реализующим интерфейс IDocumentManager<Document>.
Обобщенные типы TDocument и TDocumentManager теперь используются в реализации:
public class ProcessDocument<TDocument, TDocumentManager>
where TDocument: IDocument
where TDocumentManager: IDocumentManager<TDocument>
{
private TDocumentManager documentManager;
public static void Start(TDocumentManager dm)
{
new Thread(new ProcessDocument<TDocument,
TDocumentManager>(dm).Run).Start();
}
protected ProcessDocument (TDocumentManager dm)
{
documentManager = dm;
}
protected void Run()
{
while (true)
{
if (documentManager.IsDocumentAvailable)
{
TDocument doc = documentManager.GetDocument();
Console.WriteLine("Обработка документа {0}",
doc.Title);
}
Thread.Sleep(new Random().Next(10));
}
}
}
В методе Main() DocumentManager теперь инициируется классом
Document. Статский метод Start() класса ProcessDocuments вызывается с передачей класса Document в качестве параметра TDocument, и класса
DocumentManager<Document> в качестве параметра TDocumentManager:
class Program
{
static void Main(string[] args)
{
DocumentManager<Document> dm = new DocumentManager<Document>();
for (int i = 0; i < 100; i++)
{
Document doc = new Document("Документ " + i.ToString(), "Содержание " + i.ToString());
dm.AddDocument(doc);
Thread.Sleep(new Random().Next(10));
}
Console.WriteLine("\nФормирование документов завершено\n");
ProcessDocument<Document, DocumentManager<Document>>.Start(dm);
for (int i = 101; i < 110; i++)
{
Document doc = new Document("Документ "+i.ToString(), "Содержание "+i.ToString());
dm.AddDocument(doc);
Console.WriteLine("Добавленный документ {0}", doc.Title);
Thread.Sleep(new Random().Next(20));
}
}
}
При запуске приложения документы добавляются и извлекаются из очереди, как показано на рис. 7.7.
а) начало вывода | б) окончание вывода |
Рис. 7.7. Добавление и извлечение из очереди документов для обобщенных классов
В том случае, если нужно будет использовать новые объекты обобщенных типов при создании экземпляров обобщенных классов, конструкция where может быть расширена добавлением ограничения конструктора new(). Это ограничение конструктора говорит о том, что в обобщенном типе должен присутствовать конструктор по молчанию:
public class MyClass<T>
where T: IFoo, new() { //-..
В.NET ограничения конструктора могут указывать на необходимость наличия только конструктора по умолчанию. Невозможно определить ограничение конструктора для других конструкторов.
Дата добавления: 2015-07-07; просмотров: 124 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Преобразования типов | | | Обобщенные методы |