开发者

Static IEnumerable?

I'm trying to make an enum of strings. Here's what I've got so far,

private class TypedEnum<T> : IEnumerable<T>
{
    public IEnumerator<T> GetEnumerator()
    {
        return GetType().GetFields().Where(f => f.IsLiteral).Select(f => f.GetValue(null)).OfType<T>().GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

private static class Combinators : TypedEnum<char>
{
    public const char DirectChild = '>';
    public const char NextAdjacent = '+开发者_JS百科';
    public const char NextSiblings = '~';
    public const char Descendant = ' ';
}

But there are two problems with this. (1) It won't compile because Combinators is static... I can remove that and hide the c'tor though. (2) It's not enumerable unless I instantiate it, which I don't need nor want to do. What are my options? Forget about making it enumerable?


I think this is as close as I can get

public struct Combinators
{
    public const char DirectChild = '>';
    public const char NextAdjacent = '+';
    public const char NextSiblings = '~';
    public const char Descendant = ' ';

    public static IEnumerable<char> ToEnumerable()
    {
        return typeof(Combinators).GetFields().Where(f => f.IsLiteral)
            .Select(f => f.GetValue(null)).OfType<char>();
    }
}

But I wish I could put that ToEnumerable() method in a subclass :(


Hah! Solved it!

public class TypedEnum<TBase, TValue>
{
    public static IEnumerable<TValue> ToEnumerable()
    {
        return typeof (TBase).GetFields(BindingFlags.Public | BindingFlags.Static)
            .Select(f => f.GetValue(null)).OfType<TValue>();
    }

    public static TValue[] ToArray()
    {
        return ToEnumerable().ToArray();
    }

    public static string Pattern
    {
        get
        {
            return string.Format("(?:{0})", string.Join("|", ToEnumerable().Select(c => Regex.Escape(c.ToString()))));
        }
    }
}

public class Combinators : TypedEnum<Combinators, char>
{
    public const char DirectChild = '>';
    public const char NextAdjacent = '+';
    public const char NextSiblings = '~';
    public const char Descendant = ' ';
}

Just have to pass the class itself as a type to TypedEnum.


I removed inheritance from your Combinators class

static class Combinators { // no inheritance

Note: These solutions use a C# iterator which is simpler than a full-fledged Enumerator implementation; iterators just need the yield keyword.


Static implementation

USAGE: To iterate over the static class fields, and gathering the output:

StringBuilder sb = new StringBuilder();
foreach ( var ch in Util.AsEnumerable<char>(typeof(Combinators)) )
        sb.AppendLine(ch.ToString());

IMPLEMENTATION: A C# iterator to faciliate the feature (it resuses some of the original code):

static public class Util {

    static public IEnumerable<T> AsEnumerable<T>(Type t)
    {
        if (ReferenceEquals(null, t))
            yield break;

        foreach (T val in t.GetFields().Where(f => f.IsLiteral).Select(f => f.GetValue(null)).OfType<T>())
            yield return val;
    }
}

Result

Results of the StringBuilder receiving the fields of your Combinator class are:

>
+
~
(space)


Instance implementation

(NOTE: - Not needed to answer this question, but added for posterity and as a counterpart to the static method shown above)

USAGE: Method called as an extension on any instance, gathers results in StringBuilder:

StringBuilder sb = new StringBuilder();
foreach (var ch in new Combinators().AsEnumerable<char>())
    sb.AppendLine(ch.ToString());

IMPLEMENTATION: C# Extension method to faciliate instance enumeration:

static public class EXTENSIONS
{
    static public IEnumerable<T> AsEnumerable<T>(this object thisObj) {
        if (ReferenceEquals(null, thisObj))
            yield break;
        foreach (T val in thisObj.GetType().GetFields().Where(f => f.IsLiteral).Select(f => f.GetValue(null)).OfType<T>())
            yield return val;
    }

}


It's not possible without creating instance - a type itself cannot implement interface. The best option will be something like

private class Combinators : TypedEnum<char>
{
    public static readonly Combinators Instance = new Combinators();

    private Combinators()
    {
    }

    public const char DirectChild = '>';
    public const char NextAdjacent = '+';
    public const char NextSiblings = '~';
    public const char Descendant = ' ';
}

so you'll have single instance which can be enumerated, passed to methods, etc.

Alternatively, you can write an extension for Type:

public static class TypeExtensions
{
    public static IEnumerable<T> EnumerateConstantFields<T>(this Type type)
    {
        return type.GetFields().Where(f => f.IsLiteral).Select(f => f.GetValue(null)).OfType<T>();
    }
}

and use it like typeof(Combinators).EnumerateConstantFields<char>()


IEnumerable is helpful ONLY when running through the elements of a set in a for-each loop. Nothing else!

All arrays are IEnumerable. It has no sense, repeat, no sense, to declare IEnumerable a type that only exposes a set of constant fields.

If you have a reference to an array of your type, it's automatically IEnumerable. If you ever wanted to be able to run through the possible values of your enumeration, that's another matter. You will be instantiating an array.

Example

public enum Operators
{
    Plus,
    Minus,
    Multiply,
    Divide,
    Power
}


string[] available_ops = Enum.GetNames(typeof(Operators));
foreach(string op in available_ops) //IEnumerable<string> working here
    Console.WriteLine("Available operator: {0}", op);

Output

    Available operator: Plus
    Available operator: Minus
    Available operator: Multiply
    Available operator: Divide
    Available operator: Power
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜