Simulating Polymorphic/Generic Enumerations - C#

.net, c#, enums, generics, polymorphism

Solution

Eric Lippert will come to my house and beat me with a stick for this, but it works.

Usage:

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

or

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

Implementation:

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

with

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

Problem

I know it's a bizarre title, and i know inheritance is not possible with enums, so let me explain. If i have the following classes: - Fruit (abstract) - Apple (concrete - derived from Fruit) - Orange (concrete - derived from Fruit) And i have the following method, implemented with generics: ``` public ICollection<T> FindFruit<T>() where T : Fruit { return _fruitRepository .Fruits .OfType<T>() .ToList(); } ``` And i use it like this: ``` var apples = _fruitServices.FindFruit<Apple>(); ``` All fine. Now - i currently have the following enum: ``` public enum FruitAssociations { Color, Manufacturers } ``` Basically a "Fruit" can have many associations, which designates what associations to include in the result from my repository. So the `FindFruit` method above is actually like this: ``` public ICollection<T> FindFruit<T>(FruitAssociations[] assocs) where T : Fruit { return _fruitRepository .Fruits .OfType<T>() .IncludeAssocs(assocs) // ObjectQuery<Fruit> extension method. .ToList(); } ``` Here is that extension method: ``` public static ObjectQuery<Fruit> IncludeAssocs(this ObjectQuery<Fruit> source, FruitAssociations[] assocs) { if (assocs.Contains(FruitAssociations.Color)) query = query.Include("Color"); // etc } ``` Also works fine. The problem i am now facing however is i have associations on particular Fruit. E.g ``` public enum OrangeAssociations { OrangeFamilies } ``` And i'm not sure how to have a single `FindFruit` method that is capable of returning associations based on the type of T. This is the end result i would like to be able to do: ``` var oranges = _fruitServices.FindFruit<Orange>(new[] { OrangeAssociations.OrangeFamilies }); var apples = _fruitServices.FindFruit<Apple>(new[] { AppleAssociations.AppleFamilies }); ``` But i can't figure out how to do that without having seperate `FindFruit` methods for each fruit type, and hence defeating the point of the generic T type parameter. For those who are curious as to what i'm doing here - i'm using eager loading with Entity Framework 4, and therefore using the `.Include` method (which takes a string designating the navigational property). So my service accepts an array of enumerations for a given entity, then i use an extension method to translate that to a string used in the `.Include` statement. I'm thinking the solution is to instead of accepting an array of enums, accept a generic class: ``` public ICollection<T> FindFruit<T>(FruitAssociations<T> assocs) where T : Fruit ``` And use it like this: ``` var associations = new FruitAssociations<Orange>(OrangeAssociations.OrangeFamilies); var apples = _fruitServices.FindFruit<Apple>(associations); ``` But i'm not sure how that would work, or how to implement it. Hopefully my question makes sense and is not too long/complicated.

Original source

Related problems