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
精彩评论