模拟多态/通用枚举 - C#

时间:2010-12-09 00:53:24

标签: c# .net generics enums polymorphism

我知道这是一个奇怪的标题,我知道枚举不可能继承,所以让我解释一下。

如果我有以下课程:

  • 水果(摘要)
    • Apple(混凝土 - 来自水果)
    • 橙色(混凝土 - 来自水果)

我有以下方法,用泛型实现:

public ICollection<T> FindFruit<T>() where T : Fruit
{
   return _fruitRepository
             .Fruits
             .OfType<T>()
             .ToList();
}

我这样用它:

var apples = _fruitServices.FindFruit<Apple>();

一切都很好。

现在 - 我目前有以下枚举:

public enum FruitAssociations
{
   Color,
   Manufacturers
}

基本上,“Fruit”可以有许多关联,它指定要包含在我的存储库结果中的关联。

所以上面的FindFruit方法实际上是这样的:

public ICollection<T> FindFruit<T>(FruitAssociations[] assocs) where T : Fruit
{
   return _fruitRepository
             .Fruits
             .OfType<T>()
             .IncludeAssocs(assocs) // ObjectQuery<Fruit> extension method.
             .ToList();
}

这是扩展方法:

public static ObjectQuery<Fruit> IncludeAssocs(this ObjectQuery<Fruit> source, FruitAssociations[] assocs)
{
   if (assocs.Contains(FruitAssociations.Color))
      query = query.Include("Color");
   // etc      
}

也工作正常。我现在面临的问题是我在特定 Fruit上有关联。

E.g

public enum OrangeAssociations
{
   OrangeFamilies
}

我不知道如何使用一个FindFruit方法能够根据T的类型返回关联。

这是我希望能够做到的最终结果:

var oranges = _fruitServices.FindFruit<Orange>(new[] { OrangeAssociations.OrangeFamilies });
var apples = _fruitServices.FindFruit<Apple>(new[] { AppleAssociations.AppleFamilies });

但是我无法弄清楚如何在不使用每个水果类型的单独FindFruit方法的情况下做到这一点,从而打败通用T类型参数的点。

对于那些对我在这里做什么感到好奇的人 - 我正在使用主动加载Entity Framework 4,因此使用.Include方法(它采用指定导航属性的字符串)。所以我的服务接受给定实体的枚举数组,然后我使用扩展方法将其转换为.Include语句中使用的字符串。

我认为解决方案是不接受枚举数组,而是接受泛型类:

public ICollection<T> FindFruit<T>(FruitAssociations<T> assocs) where T : Fruit

并像这样使用它:

var associations = new FruitAssociations<Orange>(OrangeAssociations.OrangeFamilies);
var apples = _fruitServices.FindFruit<Apple>(associations);

但我不确定这是如何工作的,或者如何实现它。

希望我的问题有道理并且不会太长/太复杂。

3 个答案:

答案 0 :(得分:4)

Eric Lippert将来到我家,用棍子打我,但它确实有效。

用法:

// finds fruits of type Orange, includes Color and OrangeFamilies
var result = FindFruit(OrangeAssociation.Color,
                       OrangeAssociation.OrangeFamilies);

// finds fruits of type Fruit, includes Manufacturers
var result = FindFruit(FruitAssociation.Manufacturers);

实现:

static ICollection<TFruit> FindFruit<TAssoc, TFruit>(
    params FruitAssociation<TAssoc, TFruit>[] assocs)
    where TAssoc : FruitAssociation<TAssoc, TFruit>, new()
    where TFruit : Fruit
{
    var query = _fruitRepository.Fruits.OfType<TFruit>();

    foreach (var assoc in assocs)
    {
        query = query.Include(assoc.Name);
    }

    return query.ToList();
}

abstract class FruitAssociation<TAssoc, TFruit>
    where TAssoc : FruitAssociation<TAssoc, TFruit>, new()
    where TFruit : Fruit
{
    public static readonly TAssoc Color = Define("Color");

    public static readonly TAssoc Manufacturers = Define("Manufacturers");

    protected static TAssoc Define(string name)
    {
        return new TAssoc { Name = name };
    }

    public string Name
    {
        get;
        private set;
    }
}

sealed class FruitAssociation : FruitAssociation<FruitAssociation, Fruit>
{
}

sealed class OrangeAssociation : FruitAssociation<OrangeAssociation, Orange>
{
    public static readonly OrangeAssociation OrangeFamilies =
        Define("OrangeFamilies");
}

答案 1 :(得分:2)

你遇到的情况是Enum和Generics并不总能发挥出色。但是,您可以执行以下操作:

public static ObjectQuery<Fruit> IncludeAssocs(
    this ObjectQuery<Fruit> source, Enum[] assocs) 
{
    foreach(var assoc in assocs)
    {
        query = query.Include(assoc.ToAssociationQueryString());
    }
}

在这种情况下,我建议定义一个新属性,例如AssociationQueryString,并将其应用于这样的枚举:

public enum OrangeAssociations
{
    [AssociationQueryString("Orange Families")]
    OrangeFamilies
}

这是自定义属性:

public class AssociationQueryStringAttribute : System.Attribute
{
    private readonly string value;
    public string Value { get { return this.value; } }

    public AssociationQueryStringAttribute(string value)
    {
        this.value = value;
    }
}

以下是返回属性值的.ToAssociationQueryString()扩展方法:

public string ToAssociationQueryString(this Enum value)
{
    FieldInfo fi = value.GetType().GetField(value.ToString());
    AssociationQueryStringAttribute[] attributes = 
        (AssociationQueryStringAttribute[])fi.GetCustomAttributes(
            typeof(AssociationQueryStringAttribute), false);
    if (attributes.Length > 0)
    {
        return attributes[0].Value;
    }
    else
    {
        throw new InvalidOperationException(
            "Must use an enum decorated with the AssociationQueryString attribute.");
    }
}

答案 2 :(得分:0)

枚举并不像每个人都假设的那样神奇。它们只是一个带有一堆静态只读字段和私有构造函数的类型。如果要继承,请使用普通类和公共静态字段或返回具有所需值的类实例的属性。

Example from another question