奇怪的重复模板模式,多层继承

时间:2015-10-02 14:59:46

标签: c# generics inheritance crtp

基于完成的工作here,我已经为枚举定义了一个通用的抽象基类,如下所示:

public abstract class Enumeration<T> : IEquatable<T> where T : Enumeration<T>
{
    private static IEnumerable<T> enumerateAllValues()
    {
        // Obviously use some caching here
        var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
        return fields.Select(f => f.GetValue(null)).OfType<T>();
    }

    internal static IEnumerable<T> AllValues {get { return enumerateAllValues();}}

    protected Enumeration(int value, string displayName)
    {
        if (!typeof(T).IsSealed)
            throw new NotSupportedException($"Value objects must be sealed, {typeof(T).Name} is not.");
        this.Value = value;
        this.DisplayName = displayName;
    }

    protected int Value { get; }

    protected string DisplayName { get; }

    public override string ToString() { return DisplayName; }       
    // IEquatable implementation based solely on this.Value
}

一个静态的非泛型辅助类,用于解析和列出枚举值:

public static class Enumeration 
{
    public static IEnumerable<T> GetAllValues<T>() where T : Enumeration<T>
        {
            return Enumeration<T>.AllValues;
        }
    // Other helper methods, e.g. T Parse(int), bool TryParse(int, out T), etc.
}

现在,我从另一个抽象类派生出来,以表示某些具有共同点的枚举类:

public abstract class AnimalTrait<T> : Enumeration<AnimalTrait<T>>
{
    protected AnimalTrait(int value, string displayName) : base(value, displayName) { ; }
}

到目前为止一切顺利。作为一个例子,由此得出的具体类可能是DogTrait或FishTrait等。知道所有动物特征都可以与一个值配对,并假设动物特征的值总是一个字符串,然后我定义另一个抽象类像这样:

public struct AnimalTraitValuePair<TAnimalTrait> where TAnimalTrait : AnimalTrait<TAnimalTrait>
{

    public TAnimalTrait AnimalTrait { get; }

    public string Value { get; } // Analogy breaks down here, but lets assume we know that the values of animal traits are always strings.

    public AnimalTraitValuePair(TAnimalTrait animalTrait, string value)
    {
        this.AnimalTrait = animalTrait;
        this.Value = value;
    }

    public override string ToString()
    {
        return $"[{AnimalTrait}, {Value}]";
    }
}

与从KeyValuePair<TAnimalTrait, string> where TAnimalTrait : AnimalTrait<TAnimalTrait>派生类似,如果它不是结构,我会这样做。

现在,当我去定义包含动物名称的Animal类及其相关值的AnimalTrait列表,即AnimalTraitValuePair<TAnimal>列表时,我遇到了一个问题:

public abstract class Animal<TAnimal, TAnimalTrait> :
    where TAnimal : Animal<TAnimal, TAnimalTrait>
    where TAnimalTrait : AnimalTrait<TAnimalTrait>
{
    private readonly IList<AnimalTraitValuePair<TAnimalTrait>> animalTraitValuePairList;

    // All animals have a name
    public string Name {get;}

    protected Animal(string name, IEnumerable<AnimalTraitValuePair<TAnimalTrait>> animalTraitValuePairs)
    {
        animalTraitValuePairList = animalTraitValuePairs.ToList();
        this.Name = name;
    }

    public string this[TAnimalTrait animalTrait]
    {
        get
        {
            return animalTraitValuePairList.First(atvp => atvp.AnimalTrait == animalTrait).Value;
        }
    }

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        // !!!! BREAKS HERE !!!!
        foreach (var animalTrait in Enumeration.GetAllValues<AnimalTrait<TAnimalTrait>>()) // This works...
        //foreach (var animalTrait in Enumeration.GetAllValues<TAnimalTrait>()) // ...but this doesn't
        {
            sb.AppendLine($"{this.Name}'s traits:");
            sb.AppendLine($"[{animalTrait}, {animalTrait.Value}]");
        }
        return sb.ToString();
    }
}

我收到此编译器错误:

  

The type 'TAnimalTrait' cannot be used as type parameter 'T' in the generic type or method 'Enumeration.GetAllValues<T>()'. There is no implicit reference conversion from 'TAnimalTrait' to 'Maxim.Common.Enums.Enumeration<TAnimalTrait>'

为什么我不能直接使用TAnimalTrait?是不是TAnimalTrait被限制为AnimalTrait<TAnimalTrait>类,我们知道它是一个枚举,因此可以在基础Enumeration<T>上升两个级别?编译“正确”的那个并给我我想要的行为吗?

2 个答案:

答案 0 :(得分:1)

您的代码存在许多问题,我忘记了所有必须更改的内容,但这是一个有用的代码段:

$old_timestamp = strtotime($old_date);
$new_Date = date('jS F Y H:i A T O', filemtime($old_timestamp));

输出:

  菲法的特点:
  [颜色,黑色]
  [大小,中等]

答案 1 :(得分:0)

您对AnimalTraitValuePair

有约束
public struct AnimalTraitValuePair<TAnimalTrait> 
   where TAnimalTrait : AnimalTrait<TAnimalTrait>

当你使用它时,你传递的是动物约束的TAnimal

public abstract class Animal<TAnimal, TAnimalTrait> 
    : IEnumerable<AnimalTraitValuePair<TAnimal>>
    where TAnimal : Animal<TAnimal, TAnimalTrait>
    where TAnimalTrait : AnimalTrait<TAnimalTrait>

如果您将其更改为以下内容:

public abstract class Animal<TAnimal, TAnimalTrait> 
:  IEnumerable<AnimalTraitValuePair<TAnimalTrait>>
    where TAnimal : Animal<TAnimal, TAnimalTrait>
    where TAnimalTrait : AnimalTrait<TAnimalTrait>

您将收到一条错误消息

Enumeration<AnimalTrait<TAnimalTrait>>.Value is inaccessable due to its protection level. 

这是因为您的Animal类不是从Enumeration&lt; AnimalTraitValuePair&lt; TAnimalTrait&gt;&gt;

派生的

老实说,是IList&lt; T&gt;是IEnumerable&lt; T&gt;的通用实现,如果您想要一个实现相同目标的简单实现,我只需执行以下操作:

public class Animal
{
   private IList<AnimalTrait> _traits;
   public Animal(IList<AnimalTrait> traits)
   {
       _traits = traits;
   }
   public IEnumerable<AnimalTrait> Traits{get{return _traits;}}
}

public class AnimalTrait
{
   public int Value{get;set;}
   public string DisplayName{get;set;}
}