65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中的多重继承

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (23投票s)

2008年3月27日

CPOL

3分钟阅读

viewsIcon

127963

downloadIcon

1200

属性可用于为 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 的方法(CheckIs 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);
} 

可能的增强

  1. 使用的所有 attribute 类都可以有一个共同的接口,或者可以从一个共同的 attribute 类继承,以将它们与其他非 MI attributes 分开。
  2. FieldInfo 类列表(PropertyInfo 等)可以缓存所有常规字段和 attribute 字段,以创建更真实的多重继承。

限制

  • 明显的限制是,多重继承不能用于已经继承的类。我曾尝试创建 C# 3.0 的扩展方法,这些方法可以访问任何对象。事实证明这是不可能的,因为无法从一个通用对象中获取特定类实例的自定义属性。
  • 另一个限制是,一个“插槽”继承——原始的 C# 继承——被使用了。这应该由多重继承本身来补偿。
  • 令人头疼的是需要使用方法括号来调用多重继承方法。这是由于 C# 设计所禁止的泛型属性的缺失造成的。
  • 编译器无法捕捉 Use<> 类中的不一致。这必须由 Is<> 检查或程序员本人处理。

历史

  • 2008-03-27:文章中的泛型已更正
  • 2008-03-26:发布了初稿
© . All rights reserved.