2010-12-09 78 views
3

我知道這是一個奇怪的標題,我知道繼承是不可能的枚舉,所以讓我解釋一下。模擬多態/泛型枚舉 - C#

,如果我有以下類:

  • 水果(抽象)
    • 蘋果(混凝土 - 從水果派生)
    • 橙色(混凝土 - 從水果派生)

我有以下方法,用泛型實現:

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

我用這樣的:

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

一切都很好。

現在 - 我現在有以下枚舉:

public enum FruitAssociations 
{ 
    Color, 
    Manufacturers 
} 

基本上是一個「果」可以有很多協會,指定什麼協會從我的倉庫,結果包括。

所以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  
} 

也能正常工作。然而,我現在面臨的問題是我有特別水果協會。

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); 

,但我不確定這將如何工作,或如何實施它。

希望我的問題是有道理的,不是太長/複雜。

回答

4

埃裏克利珀特會來我家,用這根棍子打敗我,但它很有效。

用法:

// 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"); 
} 
2

你正在遇到Enum和泛型不能很好地運行的情況。但是,你可以這樣做:

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."); 
    } 
} 
+0

有趣的...雖然我真的儘量避免屬性(與反思 - 因爲它看起來你的使用等)。我會盡力去做。 – RPM1984 2010-12-09 01:36:50

+0

@ RPM1984:我認爲你有一些原因,你必須使用枚舉。如果你真的沒有這個要求,並且你可以使用類,那麼通過一切手段,類對泛型更好。 – 2010-12-09 01:42:14

+0

還要記住,不要使用反射,你可以檢查Enum類型,將其轉換,然後切換到適當的方法。 – 2010-12-09 01:43:35

0

枚舉並不像每個人都假設的那樣神奇。它們只是一個帶有一堆靜態只讀字段和一個私有構造函數的類型。如果要繼承,請使用普通類以及公共靜態字段或使用所需值返回該類實例的屬性。

Example from another question