C# 中的多重继承
属性可用于为 C# 类提供多重继承功能。
引言
本文展示了在 C# 代码中实现多重继承 (MI) 的一种可能方法。它在设计上是完全显式的,并且缺少一些经典的多重继承问题,例如继承顺序。
背景
在 C# 中有多种避免或克服多重继承需求的方法。但有时,尤其是在较小的项目中(如游戏等),利用其优势会更可行。我的 RL 游戏就是这种情况。所以我希望我找到了一个合理的方法,并且不会受到原生 C# 代码在多重继承方面设置的障碍的干扰。
我并不认为多重继承是一种好的编程模式,它应该只在模式的优点大于缺点时使用。
关注的类
首先,我们需要定义智能的祖先类,它将处理实现多重继承所需的所有功能。这并不复杂。缺失的继承将由 attribute
MI 来弥补。
using System;
using System.Collections.Generic;
namespace CSharpMultipleInheritance
{
public class Ancestor : MarshalByRefObject
{
当一个类继承自 Ancestor
类时,它的自定义 attributes
实例集将被收集并保存在一个字典中。这个哈希值稍后将用于访问继承的属性(方法等)。
private readonly Dictionary<Type, Object> attributes = null;
public Dictionary<Type, Object> Attributes
{
get { return attributes; }
}
public Ancestor()
{
attributes = GetAttributes(GetType());
}
private Dictionary<Type, Object> GetAttributes(Type sourceType)
{
Object[] collection = sourceType.GetCustomAttributes(true);
Dictionary<Type, Object> result = new Dictionary<Type, Object>();
foreach (Object attribute in collection)
{
Type attributeType = attribute.GetType();
if (result.ContainsKey(attributeType))
{
throw new Exception(string.Format(
STR_DupliciteAttributeFound, attributeType.Name, sourceType.Name));
}
else
{
result.Add(attributeType, attribute);
}
}
return result;
}
方法
Check
方法测试一个类是否“继承”自一个特定的 attribute
类。它相当于我们 MI 中的原生 C# IS
运算符。代码显示,它只检查属性类型是否存在于自定义属性列表中。
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;
}
实现多重继承本身最重要的函数是 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));
}
}
使用示例
我选择了维基百科上经典的多重继承示例。另一个示例可以在附加的 zip 文件中找到。
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
类继承,以将它们与其他非 MIattributes
分开。 FieldInfo
类列表(PropertyInfo
等)可以缓存所有常规字段和attribute
字段,以创建更真实的多重继承。
限制
- 明显的限制是,多重继承不能用于已经继承的类。我曾尝试创建 C# 3.0 的扩展方法,这些方法可以访问任何对象。事实证明这是不可能的,因为无法从一个通用对象中获取特定类实例的自定义属性。
- 另一个限制是,一个“插槽”继承——原始的 C# 继承——被使用了。这应该由多重继承本身来补偿。
- 令人头疼的是需要使用方法括号来调用多重继承方法。这是由于 C# 设计所禁止的泛型属性的缺失造成的。
- 编译器无法捕捉
Use<>
类中的不一致。这必须由Is<>
检查或程序员本人处理。
历史
- 2008-03-27:文章中的泛型已更正
- 2008-03-26:发布了初稿