Множественное наследование в C# - Методы
ОГЛАВЛЕНИЕ
Методы
Метод Check проверяет, является ли класс "унаследованным" от определенного класса attribute. Это эквивалентно собственному оператору C# IS в нашем MI. Как показано в коде, он только проверяет, присутствует ли тип атрибута в списке пользовательских атрибутов.
public Boolean Check<TAttribute>()
{
return attributes.ContainsKey(typeof(TAttribute));
}
Чтобы определить, содержит ли унаследованный от Ancestor класс все атрибуты как целевой тип, что означает очередной оператор IS (на этот раз действительный), используется метод Is. Вам нужно выполнить проверку на соответствие целевого типа класса, унаследованного от Ancestor.
public Boolean Is<TAncestor>() where TAncestor : Ancestor
{
Boolean result = true;
Dictionary<Type, Object> sourceList = Attributes;
Dictionary<Type, Object> destinationList = GetAttributes(typeof(TAncestor));
foreach (KeyValuePair<Type, Object> destinationPair in destinationList)
{
result = result && sourceList.ContainsKey(destinationPair.Key);
}
return result;
}
Наиболее важной функцией, которая делает возможным MI непосредственно, является метод Use. Он извлекает класс attribute, таким образом разрешая доступ к его внутренним свойствам (методам и т.д.).
public TAttribute Use<TAttribute>()
{
if (Check<TAttribute>())
{
return (TAttribute)attributes[typeof(TAttribute)];
}
else
{
throw new ArgumentNullException(string.Format(
STR_AttributeNotFound, typeof(TAttribute).Name, GetType().Name));
}
}
Примеры использования
Мы выбрали классический пример множественного наследования из Википедии. Другой пример можно посмотреть в присоединенных архивных файлах.
Классы attribute (будущие наследуемые кандидаты) определяются как стандартные attributes (атрибуты). Только поля public (вместо полей private со свойствами, объявленными как public) используются в этой статье для того, чтоб она была очевидной (понятной).
Пусть у нас есть два класса: класс Person, который показывает имя и возраст человека, и класс Worker, который определяет зарплату рабочего.
public class Person : Attribute
{
public String Name;
public Int32 Age;
}
public class Worker : Attribute
{
public Double Sallary;
}
Теперь attributes назначены нашему классу с MI. Этот класс должен быть унаследован от класса Ancestor (определен выше), чтобы использовать свой потенциал MI.
[Person, Worker]
public class Musician : Ancestor
{
public String Name; // this can be used as a musician stage name
}
Суперклассы доступны через методы класса Ancestor (Check, Is и Use), как показано в следующем примере:
static void Main()
{
Musician bono = new Musician();
bono.Use<Worker>().Name = "Bono";
bono.Use<Person>().Name = "Paul David Hewson";
bono.Use<Person>().Age = 47;
if (bono.Is<Musician>()) Console.WriteLine("{0} is musician.",
bono.Use<Worker>().Name);
if (bono.Check<Person>()) Console.WriteLine("His age is {0}",
bono.Use<Person>().Age);
}
Возможное улучшение
- Все использованные классы attribute могут иметь общий интерфейс или быть унаследованы от общего класса attribute, чтобы отделить их от других классов attributes, не поддерживающих множественное наследование.
- Список классов FieldInfo (PropertyInfo и т.д.) может быть кэширован через все стандартные поля и также поля attribute, чтобы создать еще более реальное множественное наследование.
Ограничения
- Очевидное ограничение состоит в том, что MI нельзя использовать для уже унаследованных классов. Мы пытались сделать расширение методов C# 3.0, которые могли бы получить доступ к любому объекту. Мы также убедились, что это невозможно, потому что нет способа получить пользовательские атрибуты экземпляра определенного класса из общего объекта.
- Другим ограничением является то, что используется один "слот" наследования – оригинальный из C#. Это должно компенсироваться непосредственно самим MI.
- Надоедает необходимость использовать круглые скобки для методов MI. Это вызвано недостатком общих свойств, которые запрещены конструкцией C#.
- Компилятор неспособен уловить несоответствия в классе Use<>. Это необходимо устранить с помощью проверок Is<> или непосредственно самому программисту.