Опишем некоторый интерфейс, задающий дополнительные свойства объектов класса:
public interface IProps{
void Prop1(string s);
void Prop2 (string name, int val);
}
Класс, наследующий интерфейс и реализующий его методы, может реализовать их явно, объявляя соответствующие методы класса открытыми:
public class Clain:IProps{
public Clain() {}
public void Prop1(string s) {
Console.WriteLine(s);
}
public void Prop2(string name, int val) {
Console.WriteLine("name = {0}, val ={1}", name, val);
}
}
Другая стратегия реализации состоит в том, чтобы некоторые методы интерфейса сделать закрытыми. Для реализации этой стратегии класс, наследующий интерфейс, объявляет методы без модификатора доступа, что по умолчанию соответствует модификатору private, и уточняет имя метода именем интерфейса:
public class ClainP:IProps{
public ClainP(){ }
void IProps.Prop1(string s) {
Console.WriteLine(s);
}
void IProps.Prop2(string name, int val) {
Console.WriteLine("name = {0}, val ={1}", name, val);
}
}
Есть два способа получения доступа к закрытым методам:
· Обертывание. Создается открытый метод, являющийся оберткой закрытого метода.
· Кастинг. Создается объект интерфейсного класса IProps, полученный преобразованием (кастингом) объекта исходного класса ClainP. Этому объекту доступны закрытые методы интерфейса.
|
|
Вот пример обертывания закрытых методов в классе ClainP:
public void MyProp1(string s){
((IProps)this).Prop1(s);
}
public void MyProp2(string s, int x){
((IProps)this).Prop2(s, x);
}
Как видите, методы переименованы и получили другие имена, под которыми они и будут известны клиентам класса. В обертке для вызова закрытого метода пришлось использовать кастинг, приведя объект this к интерфейсному классу IProps.
Преобразование к классу интерфейса
Создать объект класса интерфейса обычным путем с использованием конструктора и операции new нельзя, но можно объявить объект интерфейсного класса и связать его с настоящим объектом путем приведения (кастинга) объекта наследника к классу интерфейса. Это преобразование задается явно. Имея объект, можно вызывать методы интерфейса - даже если они закрыты в классе, для интерфейсных объектов они являются открытыми.
public void TestClainIProps(){
Console.WriteLine("Объект класса Clain вызывает открытые методы!");
Clain clain = new Clain();
clain.Prop1(" свойство 1 объекта");
clain.Prop2("Владимир", 44);
Console.WriteLine("Объект класса IProps вызывает открытые методы!");
IProps ip = (IProps)clain;
ip.Prop1("интерфейс: свойство");
ip.Prop2 ("интерфейс: свойство",77);
Console.WriteLine("Объект класса ClainP вызывает открытые методы!");
ClainP clainp = new ClainP();
clainp.MyProp1(" свойство 1 объекта");
clainp.MyProp2("Владимир", 44);
Console.WriteLine("Объект класса IProps вызывает закрытые методы!");
IProps ipp = (IProps)clainp;
ipp.Prop1("интерфейс: свойство");
ipp.Prop2 ("интерфейс: свойство",77);
}
Проблемы множественного наследования
|
|
При множественном наследовании классов возникает ряд проблем. Они остаются и при множественном наследованииинтерфейсов, хотя становятся проще. Рассмотрим две основные проблемы - коллизию имен и наследование от общего предка.
Коллизия имен
Проблема коллизии имен возникает, когда два или более интерфейса имеют методы с одинаковыми именами и сигнатурой. Здесь возможны две стратегии - склеивание методови переименование .
- Стратегия склеивания применяется тогда, когда класс - наследник интерфейсов - полагает, что разные интерфейсы задают один и тот же метод, единая реализация которого и должна быть обеспечена наследником. В этом случае наследник строит единственную общедоступную реализацию, соответствующую методам всех интерфейсов, которые имеют единую сигнатуру.
- Другая стратегия исходит из того, что, несмотря на единую сигнатуру, методы разных интерфейсов должны быть реализованы по-разному. В этом случае необходимо переименовать конфликтующие методы. Для этого достаточно реализовать методы разных интерфейсов как закрытые, а затем открыть их с переименованием.
Пример двух интерфейсов, имеющих методы с одинаковой сигнатурой, и класса - наследника этих интерфейсов, применяющего разные стратегии реализации для конфликтующих методов.
// Программа 5. Коллизии имен в интерфейсах
public interface IProps{
void Prop1(string s);
void Prop2 (string name, int val);
void Prop3();
}
public interface IPropsOne{
void Prop1(string s);
void Prop2 (int val);
void Prop3();
}
У двух интерфейсов - по три метода с совпадающими именами, сигнатуры двух методов совпадают, а в одном случае различаются. Вот класс, наследующий оба интерфейса:
public class ClainTwo:IProps,IPropsOne {
// склеивание методов двух интерфейсов
public void Prop1 (string s) {
Console.WriteLine(s);
}
// перегрузка методов двух интерфейсов
public void Prop2(string s, int x) {
Console.WriteLine(s + "; " + x);
}
public void Prop2 (int x) {
Console.WriteLine(x);
}
// private реализация и переименование методов двух интерфейсов
void IProps.Prop3() {
Console.WriteLine("Метод 3 интерфейса 1");
}
void IPropsOne.Prop3() {
Console.WriteLine("Метод 3 интерфейса 2");
}
public void Prop3FromInterface1() {
((IProps)this).Prop3();
}
public void Prop3FromInterface2() {
((IPropsOne)this).Prop3();
}
}
public void TestCliTwoInterfaces(){
Console.WriteLine("Объект сlainTwo вызывает методы двух интерфейсов!");
ClainTwo claintwo = new ClainTwo();
claintwo.Prop1("Склейка свойства двух интерфейсов");
claintwo.Prop2("перегрузка.: ",99);
claintwo.Prop2(9999);
claintwo.Prop3FromInterface1();
claintwo.Prop3FromInterface2();
Console.WriteLine("Интерфейсный объект вызывает методы 1-го интерфейса!");
IProps ip1 = (IProps)claintwo;
ip1.Prop1("интерфейс IProps: свойство 1");
ip1.Prop2("интерфейс 1 ", 88);
ip1.Prop3();
Console.WriteLine("Интерфейсный объект вызывает методы 2-го интерфейса!");
IPropsOne ip2 = (IPropsOne)claintwo;
ip2.Prop1("интерфейс IPropsOne: свойство1");
ip2.Prop2(7777);
ip2.Prop3();
}