Ограничения универсальных типов

Допустим, мы хотим, чтобы класс Bank хранил набор счетов, представленных объектами некоторого класса Account:

class Account

{

           public int Id { get; private set;} // номер счета

public Account(int _id)

{

   Id = _id;

}

}

Но у этого класса может быть много наследников: DepositAccount (депозитный счет), DemandAccount (счет до востребования) и т.д. Однако мы не можем знать, какой вид счета в банке в данном случае будет использоваться. Возможно, банк будет принимать только депозитные счета. И в этом случае в качестве универсального параметра можно установить тип Account:

class Bank<T> where T: Account

{

T[] accounts;

 

public Bank(T[] accs)

{

   this.accounts = accs;

}

           // вывод информации обо всех аккаунтах

public void AccountsInfo()

{

   foreach(Account acc in accounts)

   {

       Console.WriteLine(acc.Id);

   }

}

}

С помощью выражения where T: Account мы указываем, что используемый тип T обязательно должен быть классом Account или его наследником.

Теперь применим класс Bank в программе:

class Program

{

static void Main(string[] args)

{

   Account[] accounts = new Account[]

   {

       new Account(1857), new Account(2225), new Account(33232)

   };

   Bank<Account> bank = new Bank<Account>(accounts);

   bank.AccountsInfo();

 

   Console.ReadLine();

}

}

При этом мы можем задать множество ограничений через запятую:

class Client { }

interface IAccount { }

interface ITransfer { }

class Bank<T> where T: Client, IAccount, ITransfer

{}

Следует учитывать, что в данном случае только один класс может использоваться в качестве ограничения, в то время как интерфейсов может применяться несколько. При этом класс должен указываться первым.

Кроме того, можно указать ограничение, чтобы использовались только структуры:

class Bank<T> where T: struct

{}

или классы:

class Bank<T> where T: class

{}

А также можно задать в качестве ограничения класс или структуру, которые реализуют конструктор по умолчанию с помощью слова new:

class Bank<T> where T: new()

{}

Использование нескольких универсальных параметров

Мы можем также задать сразу несколько универсальных параметров и ограничения к каждому из них:

class Operation<T, U>

where U: class

where T: Account, new() { }

Обобщенные методы

Кроме обобщенных классов можно также создавать обобщенные методы, которые точно также будут использовать универсальные параметры. Например:

class Program

{

static void Main(string[] args)

{

   Client client2 = new Client("Том", "Симпсон");

 

   Display<Client>(client2);

       

   Console.ReadLine();

}

 

private static void Display<T>(T person) where T: Person

{

   person.Display();

}

}

abstract class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

 

public Person(string lName, string fName)

{

   FirstName = fName;

                          LastName = lName;

}

 

public abstract void Display();

}

class Client: Person

{

public Client(string lName, string fName)

      : base(fName, lName)

{

           }

 

public override void Display()

{

   Console.WriteLine(FirstName + " " + LastName);

}

}

Наследование обобщенных типов

Один обобщенный класс может быть унаследован от другого обобщенного:

class Transaction<T>

{

T inAccount;

T outAccount;

 

public Transaction(T inAcc, T outAcc)

{

   inAccount = inAcc;

   outAccount = outAcc;

}

}

class UniversalTransaction<T>: Transaction<T>

{

public UniversalTransaction(T inAcc, T outAcc): base(inAcc, outAcc)

{

       

}  

}

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

class StringTransaction: Transaction<string>

{

public StringTransaction(string inAcc, string outAcc)

      : base(inAcc, outAcc)

{

           }

}

Теперь в производном классе в качестве типа будет использоваться тип string

Также производный класс может быть типизирован параметром совсем другого типа, отличного от типа базового класса:

class IntTransaction<T>: Transaction<int>

{

T operationCode = default(T);

public IntTransaction(int inAcc, int outAcc, T operCode)

      : base(inAcc, outAcc)

{

   this.operationCode = operCode;

}

}

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: