我知道这是一个奇怪的标题,我知道枚举不可能继承,所以让我解释一下。
如果我有以下课程:
我有以下方法,用泛型实现:
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);
但我不确定这是如何工作的,或者如何实现它。
希望我的问题有道理并且不会太长/太复杂。
答案 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)
枚举并不像每个人都假设的那样神奇。它们只是一个带有一堆静态只读字段和私有构造函数的类型。如果要继承,请使用普通类和公共静态字段或返回具有所需值的类实例的属性。