 |
|
 |
I didn't read through all the code, but I suspect that most of what you've done can probably be done with existing .net code:
[System.FlagsAttribute()]
public enum MyEnum
{
Value0 = 1
,
Value1 = 2
,
Value2 = 4
,
Value3 = 8
,
Value4 = 16
}
System.Collections.Generic.HashSet<MyEnum> MyEnumBlacklist =
new System.Collections.Generic.HashSet<MyEnum>() ;
MyEnumBlacklist.Add ( MyEnum.Value2 | MyEnum.Value4 ) ;
System.Console.WriteLine ( MyEnumBlacklist.Contains ( MyEnum.Value1 ) ) ;
System.Console.WriteLine ( MyEnumBlacklist.Contains ( MyEnum.Value2 ) ) ;
System.Console.WriteLine ( MyEnumBlacklist.Contains ( MyEnum.Value4 ) ) ;
System.Console.WriteLine ( MyEnumBlacklist.Contains ( MyEnum.Value2 | MyEnum.Value4 ) ) ;
Then I would likely define an Attribute to mark the enumeration with the disallowed combinations.
And write a helper class to instantiate and populate the blacklist.
I also don't see any bitwise operations in your code, for instance
(uint)Math.Pow(2, i)
could be replaced with
1u << i
modified on Saturday, July 19, 2008 3:53 PM
|
|
|
|
 |
|
 |
Well, if you would've actually compiled your example and tried making a few combinations of your MyEnum, then you would've realized it creates an enumerator with the following values:
[<span style="color:purple;font-weight:bolder;">FlagsAttribute</span>]
<span style="color:blue;">public enum</span> <span style="color:navy;">MyEnum</span>
{
Value0 = <span style="color:red;">0</span>, <span style="color:green;">//000</span>
Value1 = <span style="color:red;">1</span>, <span style="color:green;">//001</span>
Value2 = <span style="color:red;">2</span>, <span style="color:green;">//010</span>
Value3 = <span style="color:red;">3</span>, <span style="color:green;">//011</span>
Value4 = <span style="color:red;">4</span> <span style="color:green;">//100</span>
}
If you Console.WriteLine(Value1 | Value2); the values from your enum you get the following output: Value3
The problem with this is it's not flag friendly, which was the point of the article.
In order to get the flags you want, you'd need to do the values of the enumerator in powers of two, like so:
[<span style="color:purple;font-weight:bolder;">FlagsAttribute</span>]
<span style="color:blue;">public enum</span> <span style="color:navy;">MyEnum</span>
{
Value0 = <span style="color:red;">1</span>, <span style="color:green;">//0x01 or 00001</span>
Value1 = <span style="color:red;">2</span>, <span style="color:green;">//0x02 or 00010</span>
Value2 = <span style="color:red;">4</span>, <span style="color:green;">//0x04 or 00100</span>
Value3 = <span style="color:red;">8</span>, <span style="color:green;">//0x08 or 01000</span>
Value4 = <span style="color:red;">16</span> <span style="color:green;">//0x10 or 10000</span>
}
The article text even mentioned exactly how the value is generated, I used the Math.Pow(2, i) to help illustrate this relationship to the binary nature of the solution. The bit-wise operations come in if you read the code:
result |= (<span style="color:blue;">uint</span>)<span style="color:purple;font-weight:bolder;">Math</span>.Pow(<span style="color:red;">2</span>, i);
That's an instruction to or-assign the result to the value on the right side. Which means basically:
result = result | (<span style="color:blue;">uint</span>)<span style="color:purple;font-weight:bolder;">Math</span>.Pow(2, i);
Edit (12:12 p.m.):
The article even describes how you can define the blacklist through the bits the enumerator values contain. For example, if you take the above, powers of two enumeration, and add another bit to create a bit-relational block, you get the following:
[<span style="color:purple;font-weight:bolder;">FlagsAttribute</span>]
<span style="color:blue;">public enum</span> <span style="color:navy;">MyEnum</span>
{
Value0 = <span style="color:red;">01</span>, <span style="color:green;">//0x01 or 000001</span>
Value1 = <span style="color:red;">02</span>, <span style="color:green;">//0x02 or 000010</span>
Value2 = <span style="color:red;">36</span>, <span style="color:green;">//0x24 or 100100</span>
Value3 = <span style="color:red;">08</span>, <span style="color:green;">//0x08 or 001000</span>
Value4 = <span style="color:red;">48</span>, <span style="color:green;">//0x30 or 110000</span>
}
The only reason the bit-block works as described is if you have the black-matched values Value2 and Value4 set as flags, but you remove one, you're also removing a key identifier bit from the other (the shared bit sixth bit, 0x20 or 32). If you try the following:
Console.WriteLine((MyEnum.Value2 | MyEnum.Value4) ^ MyEnum.Value2); you get 16.
modified on Sunday, July 20, 2008 1:08 AM
|
|
|
|
 |
|
 |
Ah, yes, coding too early in the morning, and performing one simple test.
I did compile and run it, and the output matched what I expected so I called it good. :(
|
|
|
|
 |
|
 |
That's fine.
I just wrote a lengthy response to you to ensure we were on the same page.
As for the Math.Pow(...), that's probably a matter of preference. Both methods work, and the project isn't focused on performance, you generate the enumeration and you're done with it. You could argue one way is better than the other, but it really doesn't matter so long as the task gets done.
Were the project about performance and reliability, I would've opted for a solution akin to what you described; however, it's not, so I didn't.
I like some of the quotes in your profile, here's one of my favorites:
"Why program by hand in five days what you can spend five years of your life automating?" Terrence Parr[^]
|
|
|
|
 |
|
 |
Allen C. Copeland Jr. wrote: isn't focused on performance
Perhaps, but... you included it in an article. That means that someone who doesn't know better may use it in a critical situation. You should think of that person when writing articles.
I have found that writing an article is a good opportunity to clean up and streamline my code, in some cases I also add functionality.
Anyway, it's a new morning, and I awoke with thoughts of blacklisted enumeration values... again. Thank you very little.
I occured to me that, with a lot of extra typing, the enumeration itself could be used as a whitelist. Have a member for every allowed combination; any combination that does not have a member is disallowed.
The simplest test I've come up with for that is: if ( val.ToString().IndexOf ( ',' ) == -1 ), however, as I've done with other enumeration tools, I'd use a Dictionary (or a HashSet) to cache the whitelist.
The main downside to this technique is that you can't disallow individual values. (e.g. Value1 must always be in combination with at least one other value.)
|
|
|
|
 |
|
 |
PIEBALDconsult wrote: Perhaps, but... you included it in an article. That means that someone who doesn't know better may use it in a critical situation. You should think of that person when writing articles.
I also knew you would say this when I posted that; very well, I'll give you that.
As for your blacklisting system, you could also create a helper class that uses Reflection.Emit to build a blacklist helper aid for a given type of enumeration. This would effectively require iterating the individual values of the enumeration and building a referential list similar to the one presented in the article (a two-dimensional boolean array with each dimension sized relative to the count of the enumeration's elements). It would ignore cases where an enumeration contains all of the bits of another element, due to special cases where the enumeration element is a full combination of other elements (the exact opposite of a black-list, a grouping enum element).
Or you could go simpler, if you wanted to omit the emission of a new type by handling the logic without building the dynamic helper. The trade off would be a little slower, but would have the benefit of reducing the code size. In reality though the black list is easiest to be handled as it is now.
The way to get your blacklist, if you were to make a helper, would be to merely iterate every element, and inside that loop, iterate every element besides the one you're on. In this nested enumeration, perform an '&' operation on the values of the two elements, if it's something other than zero, then you have an invalid set; however this is only true if the result of the operation does not contain all the bits of one of the two values.
|
|
|
|
 |
|
 |
Allen C. Copeland Jr. wrote: Reflection.Emit to build a blacklist helper
I use a generic class for things like that.
Your article should explain your technique in more detail; it doesn't even show how to set up an enumeration to use it.
When I saw that your GetSeriesValue method takes an int rather than an enum value, I thought it didn't use enumerations at all.
|
|
|
|
 |
|
 |
Sorry,
I thought it was more clear in the article, with the screen shot of the program, the fact that the data the GetSeriesValue takes is the 'index' of the element, and the fact that it was mentioned that the FlaggableEnumeration was a List with extra relational features that the method was obvious. It builds the enumeration for you using the relational data you provide. You specify the names of the elements, and which elements per element are valid combinations, and which are not (based upon the check-boxes on the right side of the dialog).
I'll update the article tomorrow with a bit more information. Right now I have to prepare for work.
|
|
|
|
 |
|
 |
Allen C. Copeland Jr. wrote: It builds the enumeration
What? Are you emitting it?
What I don't understand about emitting enums is, how can you use enumerated values in your code if they don't exist until runtime?
At any rate I'd like to see an enum that's set up for use with your code.
|
|
|
|
 |
|
 |
Allen C. Copeland Jr. wrote: I used the Math.Pow(2, i) to help illustrate
Don't; others may read that and think it's a good idea.
|
|
|
|
 |
|