Consider the following code:
using System;
namespace Test
{
enum Foo
{
A = 1,
B = 1,
C = 1
}
public static class Program
{
public static void Main()
{
Console.WriteLine("{0}, {1}, {2}", Foo.A, Foo.B, Foo.C);
}
}
}
Knowing that enums are just integers under the hood, I expected it to be either A, A, A or C, C, C. But surprisingly, it prints out B, B, B! This behaviour appears to be consistent across .NET Framework, .NET Core 3.x and .NET 5.
Why does it choose B?
It's undefined according to the documentation for Enum.GetName():
If multiple enumeration members have the same underlying value, the
GetName method guarantees that it will return the name of one of those
enumeration members. However, it does not guarantee that it will
always return the name of the same enumeration member.
So it can do what it likes in this regard.
As to why it returns B in your example, we can inspect the implementation of GetEnumName():
public virtual string GetEnumName(object value)
{
if (value == null)
throw new ArgumentNullException("value");
if (!IsEnum)
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");
Contract.EndContractBlock();
Type valueType = value.GetType();
if (!(valueType.IsEnum || Type.IsIntegerType(valueType)))
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnumBaseTypeOrEnum"), "value");
Array values = GetEnumRawConstantValues();
int index = BinarySearch(values, value);
if (index >= 0)
{
string[] names = GetEnumNames();
return names[index];
}
return null;
}
Aha! All is explained. To make the lookup faster, they used a binary search. And where is the first place a binary search looks when starting the search? That's right - it starts halfway through the list. And that's why it's finding the B first - after the list is ordered, the B in in the middle.
(Note that the list is ordered by enum value, not enum name, so for your case the list is already ordered since all the values are the same.)
Related
Before marking this as duplicate because of its title please consider the following short program:
static void Main()
{
var expected = new List<long[]> { new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
var actual = DoSomething();
if (!actual.SequenceEqual(expected)) throw new Exception();
}
static IEnumerable<long[]> DoSomething()
{
yield return new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
}
I have a method which returns a sequence of arrays of type long. To test it I wrote some test-code similar to that one within Main.
However I get the exception, but I don´t know why. Shouldn´t the expected sequence be comparable to the actually returned one or did I miss anything?
To me it looks as both the method and the epxected contain exactly one single element containing an array of type long, doesn´t it?
EDIT: So how do I achieve to not get the exception meaning to compare the elements within the enumeration to return equality?
The actual problem is the fact that you're comparing two long[], and Enumerable.SequenceEquals will use an ObjectEqualityComparer<Int64[]> (you can see that by examining EqualityComparer<long[]>.Default which is what is being internally used by Enumerable.SequenceEquals), which will compare references of those two arrays, and not the actual values stored inside the array, which obviously aren't the same.
To get around this, you could write a custom EqualityComparer<long[]>:
static void Main()
{
var expected = new List<long[]>
{ new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
var actual = DoSomething();
if (!actual.SequenceEqual(expected, new LongArrayComparer()))
throw new Exception();
}
public class LongArrayComparer : EqualityComparer<long[]>
{
public override bool Equals(long[] first, long[] second)
{
return first.SequenceEqual(second);
}
// GetHashCode implementation in the courtesy of #JonSkeet
// from http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array
public override int GetHashCode(long[] arr)
{
unchecked
{
if (array == null)
{
return 0;
}
int hash = 17;
foreach (long element in arr)
{
hash = hash * 31 + element.GetHashCode();
}
return hash;
}
}
}
No, your sequences are not equal!
Lets remove the sequence bit, and just take what is in the first element of each item
var firstExpected = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
var firstActual = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
Console.WriteLine(firstExpected == firstActual); // writes "false"
The code above is comparing two separate arrays for equality. Equality does not check the contents of arrays it checks the references for equality.
Your code using SequenceEquals is, essentially, doing the same thing. It checks the references in each case of each element in an enumerable.
SequenceEquals tests for the elements within the sequences to be identical. The elements within the enumerations are of type long[], so we actually compare two different arrays (containing the same elements however) against each other which is obsiously done by comparing their references instead of their actual value .
So what we actually check here is this expected[0] == actual[0] instead of expected[0].SequqnceEquals(actual[0])
This is obiosuly returns false as both arrays share different references.
If we flatten the hierarchy using SelectMany we get what we want:
if (!actual.SelectMany(x => x).SequenceEqual(expected.SelectMany(x => x))) throw new Exception();
EDIT:
Based on this approach I found another elegant way to check if all the elements from expected are contained in actual also:
if (!expected.All(x => actual.Any(y => y.SequenceEqual(x)))) throw new Exception();
This will search if for ever sub-list within expected there is a list within actual that is sequentially identical to the current one. This seems much smarter to be as we do not need any custom EqualityComparer and no weird hashcode-implementation.
I have 2 lists of different types. I think for now it doesn't matter what types that are.
Both types have an information about occurance which is in ticks (but can also be a DateTime).
What I want to do is, to synchronize these 2 lists by time so for example i can iterate through all elements in the order how they occured in time.
Example: // in this example List has elements called A_NUM or B_NUM depending on a type of list and number after '_' will represent order at which this elements/events occured
ListA = {A_2, A_3, A_5}
ListB = {B_1, B_4, B_6}
And the result after synchronization will be something like this:
ResultList = {B_1, A_2, A_3, B_4, A_5, B_6}
Is it somehow possible to make such mixed list? Or I have to create some auxiliary List or Dictionary which will tell me synchronized order of this 2 lists?
EDIT:
One list is a list of eye fixations. Fixation have a position, duration, ... and also occurance attributes.
Second list is a list of some changes of text, for example on line 12 column 3 there was a char 'x' added at some time t.
And I want to iterate through these 2 lists simultaneously. I mean at time t1 fixation occured at position x,y. At time t2 there was a text change at position u,v, so I want to iterate through these events in the order as they occured in time.
Note: YES both lists are sorted by time. It is a sequence of fixations and sequence of text changes.
Your question strongly suggests a merge sort as the basic implementation detail. You have two inputs, both sorted, and just want them merged together in sequence.
The main difficulty implied by your question is that you are trying to merge sequences of two completely unrelated types. Ordinarily, you'd merge sequences of the same type, and so could easily manipulate them together. Barring that, they'd at least share a base class or interface type, so that you could treat them as a single generalized type. But it seems, from your question, that this is not the case.
Given that, I think the most straight-forward approach is still to use a merge sort, but to provide a mechanism for the sort to access the relevant property (ticks, DateTime, whatever). The sort would return the merged sequences, in correct order, as the object type (i.e. the only base type common to both inputs) and the caller would then have to cast back to the individual types for whatever purpose.
Here's an example of what I mean:
private static IEnumerable<TBase> Merge<TBase, T1, T2, TValue>(
IEnumerable<T1> sequence1, IEnumerable<T2> sequence2,
Func<T1, TValue> valueSelector1, Func<T2, TValue> valueSelector2)
where T1 : TBase
where T2 : TBase
where TValue : IComparable<TValue>
{
IEnumerator<T1> enumerator1 = sequence1.GetEnumerator();
IEnumerator<T2> enumerator2 = sequence2.GetEnumerator();
bool notDone1 = enumerator1.MoveNext(),
notDone2 = enumerator2.MoveNext();
while (notDone1 && notDone2)
{
TValue value1 = valueSelector1(enumerator1.Current),
value2 = valueSelector2(enumerator2.Current);
if (value1.CompareTo(value2) <= 0)
{
yield return enumerator1.Current;
notDone1 = enumerator1.MoveNext();
}
else
{
yield return enumerator2.Current;
notDone2 = enumerator2.MoveNext();
}
}
while (notDone1)
{
yield return enumerator1.Current;
notDone1 = enumerator1.MoveNext();
}
while (notDone2)
{
yield return enumerator2.Current;
notDone2 = enumerator2.MoveNext();
}
}
Used like this:
class A
{
public int Value { get; }
public A(int value)
{
Value = value;
}
}
class B
{
public int Value { get; }
public B(int value)
{
Value = value;
}
}
static void Main(string[] args)
{
const int minCount = 5, maxCount = 15, maxValue = 50;
Random random = new Random();
int listACount = random.Next(minCount, maxCount),
listBCount = random.Next(minCount, maxCount);
A[] listA = RandomOrderedSequence(random, maxValue, listACount).Select(i => new A(i)).ToArray();
B[] listB = RandomOrderedSequence(random, maxValue, listBCount).Select(i => new B(i)).ToArray();
Console.WriteLine("listA: ");
Console.WriteLine(string.Join(", ", listA.Select(a => a.Value)));
Console.WriteLine("listB: ");
Console.WriteLine(string.Join(", ", listB.Select(b => b.Value)));
foreach (object o in Merge<object, A, B, int>(listA, listB, a => a.Value, b => b.Value))
{
A a = o as A;
if (a != null)
{
// Do something with object of type A
Console.WriteLine($"a.Value: {a.Value}");
}
else
{
// Must be a B. Do something with object of type B
B b = (B)o;
Console.WriteLine($"b.Value: {b.Value}");
}
}
}
static IEnumerable<int> RandomOrderedSequence(Random random, int max, int count)
{
return RandomSequence(random, max, count).OrderBy(i => i);
}
static IEnumerable<int> RandomSequence(Random random, int max, int count)
{
while (count-- > 0)
{
yield return random.Next(max);
}
}
In your case, you would of course replace types A and B with the types you're actually using, provide appropriate selectors, and then do whatever you like with each instance returned as the merged, in-order sequence.
Note that even if the types do turn out to share some common basis for which they can be compared and merged, I would still recommend a merge sort over simply concatenating and merging the result. The merge sort is a much more efficient way to merge already-ordered data into a single sequence of ordered data.
You need the lists to implement a common interface so they can be compared. For example:
public interface ISynchronizable
{
long GetTicks();
}
So you need to have the two object implement this, like so:
public class Fixation : ISynchronizable
{
public long GetTicks()
{
// get the ticks
}
// some other code
}
public class TextChange: ISynchronizable
{
public long GetTicks()
{
// get the ticks
}
// some other code
}
Then the result list would be created like this:
public List<ISynchronizable> list = new List<ISynchronizable>();
list.AddRange(fixationList);
list.AddRange(textChangeList);
resultList = list.OrderBy(e => e.GetTicks()).ToList();
Let us say, I have an enum BasicType, which is defined as follows:
public enum ObjectType{
A = 1,
B = 2,
C = 3,
}
The BasicType identifies performs a ternary classification of any Object. Subsequently, I realized that the objects A and B need to be treated in a similar way as compared to C, so I defined another enum ObjectGroupType as follows :
public enum ObjectGroupType
{
AB = 1,
C = 2,
}
With the new enum, I am able to bucket objects of several known types as one. So, when I receive a stream of objects in various types, I actually identify whether they belong to AB or C type. Is there an elegant workaround for this? For instance, will I be able to assign the same enum values for A and B in the ObjectGroupType?:
Edit 1 : I am unable to find the resemblance to the question here
Edit 2 : Thank you Maurice for your constructive inputs -- taking cues from your answer, I came up with this redefined ObjectGroupType.
public enum ObjectGroupType
{
AB = ObjectType.A | ObjectType.B
C = 2,
}
Is this valid?
Essentially, when I process a stream of objects of type AB, I want to ascertain Type A or Type B objects. This is quite similar to a hierarchical two-level decision tree:
object
/ \
AB C
/\
A B
I apologize in advance if I misread your intent, but it almost sounds like you want to allow multiple different enum types to be acted on in your code based on the enum value. The good thing is that you can do that already with bitwise operations and enums.
Given an enum that looks like this:
[Flags]
enum ObjectType
{
A = 1,
B = 2,
C = 4,
D = 8
}
You can set a comparison value that is the bitwise OR of several values:
var allowedValues = ObjectType.A | ObjectType.C;
This works because the values in the enum act like bit fields under the covers.
When you run your code, you do a bitwise AND on the allowedValues variable and the test variable and see if it matches your test variable. If it does, then it is one of the values you want:
if ((test & allowed) == test) ...
Below is a working example using the enum above that shows you how it works.
void Main()
{
var allowed = ObjectType.A | ObjectType.C;
var values = new int [] { 1, 2, 4, 8 };
foreach (var i in values)
{
var test = (ObjectType)i;
if ((test & allowed) == test)
{
Console.WriteLine("Found a match: {0}", test);
}
else
{
Console.WriteLine("No match: {0}", test);
}
}
}
Best of luck!
Edit:
I found the answer of Maurice Reeves very good, I only want to bring some more info:
[Flags]
public enum ObjectType
{
None=0,
A = 1,
B = 2,
C = 4,
D = 8,
E = 16,
AorB=A|B,
BorCorD=B|C|D,
}
By using [Flags] attribute, you can create sets of enum items, which can help you establishing different business rules for each set.
In order to check if and item exist in a set you can do as follow:
public static bool IsAorB(this ObjectType item)
{
return ObjectType.AorB.HasFlag(item);
}
if you want to creat on the fly new set of items, you can do:
var newGroup=ObjectType.A | ObjectType.BorCorD;
if you want to apply some business rule to a set, except an item, you can do:
var newGroupExceptC =newGroup^=ObjectType.C;
Now if you check if element C exist in the set you will get false:
bool exist=newGroupExceptC.HasFlag(ObjectType.C) // =false
more info you can find here
You might use a int instead of an enum, use values that don't overlap when combined (i.e. values whose binary representation has only one bit on) and then perform a mask operation on the ObjectType of a parameter to determine if it is AB:
class SomeClass
{
public static class ObjectType
{
public const int A = 1;
public const int B = 2;
public const int C = 4;
public const int D = 8;
}
public int MyType;
public string Title;
static void Main(string[] args)
{
List<SomeClass> list = new List<SomeClass>()
{
new SomeClass() {Title ="I am of type A", MyType = ObjectType.A }
,new SomeClass() {Title ="I am of type B", MyType = ObjectType.B }
,new SomeClass() {Title ="I am of type AB", MyType = ObjectType.A | ObjectType.B }
};
list.ForEach(p => { if (p.MyType == (ObjectType.A | ObjectType.B)) Console.WriteLine(p.Title); });
}
}
The downside of this approach is losing strong-typing of Object Type, i.e. you can assign any value not just those you define in the ObjectType.
List1 contains items { A, B } and List2 contains items { A, B, C }.
What I need is to be returned { C } when I use Except Linq extension. Instead I get returned { A, B } and if I flip the lists around in my expression the result is { A, B, C }.
Am I misunderstanding the point of Except? Is there another extension I am not seeing to use?
I have looked through and tried a number of different posts on this matter with no success thus far.
var except = List1.Except(List2); //This is the line I have thus far
EDIT: Yes I was comparing simple objects. I have never used IEqualityComparer, it was interesting to learn about.
Thanks all for the help. The problem was not implementing the comparer. The linked blog post and example below where helpful.
If you are storing reference types in your list, you have to make sure there is a way to compare the objects for equality. Otherwise they will be checked by comparing if they refer to same address.
You can implement IEqualityComparer<T> and send it as a parameter to Except() function. Here's a blog post you may find helpful.
edit: the original blog post link was broken and has been replaced above
So just for completeness...
// Except gives you the items in the first set but not the second
var InList1ButNotList2 = List1.Except(List2);
var InList2ButNotList1 = List2.Except(List1);
// Intersect gives you the items that are common to both lists
var InBothLists = List1.Intersect(List2);
Edit: Since your lists contain objects you need to pass in an IEqualityComparer for your class... Here is what your except will look like with a sample IEqualityComparer based on made up objects... :)
// Except gives you the items in the first set but not the second
var equalityComparer = new MyClassEqualityComparer();
var InList1ButNotList2 = List1.Except(List2, equalityComparer);
var InList2ButNotList1 = List2.Except(List1, equalityComparer);
// Intersect gives you the items that are common to both lists
var InBothLists = List1.Intersect(List2);
public class MyClass
{
public int i;
public int j;
}
class MyClassEqualityComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass x, MyClass y)
{
return x.i == y.i &&
x.j == y.j;
}
public int GetHashCode(MyClass obj)
{
unchecked
{
if (obj == null)
return 0;
int hashCode = obj.i.GetHashCode();
hashCode = (hashCode * 397) ^ obj.i.GetHashCode();
return hashCode;
}
}
}
You simply confused the order of arguments. I can see where this confusion arose, because the official documentation isn't as helpful as it could be:
Produces the set difference of two sequences by using the default equality comparer to compare values.
Unless you're versed in set theory, it may not be clear what a set difference actually is—it's not simply what's different between the sets. In reality, Except returns the list of elements in the first set that are not in the second set.
Try this:
var except = List2.Except(List1); // { C }
Writing a custom comparer does seem to solve the problem, but I think https://stackoverflow.com/a/12988312/10042740 is a much more simple and elegant solution.
It overwrites the GetHashCode() and Equals() methods in your object defining class, then the default comparer does its magic without extra code cluttering up the place.
Just for Ref:
I wanted to compare USB Drives connected and available to the system.
So this is the class which implements interface IEqualityComparer
public class DriveInfoEqualityComparer : IEqualityComparer<DriveInfo>
{
public bool Equals(DriveInfo x, DriveInfo y)
{
if (object.ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
// compare with Drive Level
return x.VolumeLabel.Equals(y.VolumeLabel);
}
public int GetHashCode(DriveInfo obj)
{
return obj.VolumeLabel.GetHashCode();
}
}
and you can use it like this
var newDeviceLst = DriveInfo.GetDrives()
.ToList()
.Except(inMemoryDrives, new DriveInfoEqualityComparer())
.ToList();
I'm trying to make a helper method for listing the names of all bits set in an Enum value (for logging purposes). I want have a method that would return the list of all the Enum values set in some variables. In my example
[Flag]
Enum HWResponse
{
None = 0x0,
Ready = 0x1,
Working = 0x2,
Error = 0x80,
}
I feed it 0x81, and it should provide me with a IEnumerable<HWResponse> containing {Ready, Error}.
As I didn't find a simpler way, I tried to write the code below, but I can't make it compile.
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
List<T> toreturn = new List<T>(100);
foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
{
Enum bit = ((Enum) curValueBit); // Here is the error
if (mask.HasFlag(bit))
toreturn.Add(curValueBit);
}
return toreturn;
}
On this version of the code, the compiler complains that it can't cast T to Enum.
What did I do wrong? Is there a better (simpler) way to do this? How could I make the cast?
Also, I tried to write the method as
public static IEnumerable<T> MaskToList<T>(Enum mask) where T:Enum
but Enum is of a special type that forbids the 'where' syntax (Using C# 4.0)
Here's a simple way to write it using LINQ:
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
return Enum.GetValues(typeof(T))
.Cast<Enum>()
.Where(m => mask.HasFlag(m))
.Cast<T>();
}
If your desired end result is a string list of names, just call mask.ToString().
What would you do if the enum were defined like this:
[Flags]
enum State
{
Ready = 1,
Waiting = 2,
ReadyAndWaiting = 3
}
As to resolving the compiler error, this should do it:
Enum bit = (Enum)(object)curValueBit;
Jon Skeet has a project called unconstrained melody that allows you to add the enum constraint, after compilation, by rewriting the IL. This works because the CLR supports such a constraint, even though C# does not.
Another thought: It will be more efficient to cast the return value of GetValues directly to T[]:
foreach(T curValueBit in (T[])Enum.GetValues(typeof (T)))
Building on Gabe's answer I came up with this :
public static class EnumHelper<T>
where T : struct
{
// ReSharper disable StaticFieldInGenericType
private static readonly Enum[] Values;
// ReSharper restore StaticFieldInGenericType
private static readonly T DefaultValue;
static EnumHelper()
{
var type = typeof(T);
if (type.IsSubclassOf(typeof(Enum)) == false)
{
throw new ArgumentException();
}
Values = Enum.GetValues(type).Cast<Enum>().ToArray();
DefaultValue = default(T);
}
public static T[] MaskToList(Enum mask, bool ignoreDefault = true)
{
var q = Values.Where(mask.HasFlag);
if (ignoreDefault)
{
q = q.Where(v => !v.Equals(DefaultValue));
}
return q.Cast<T>().ToArray();
}
}
I organized things a bit differently, namely I put the type check (i.e.: the verification that T is really an enumeration) and the obtaining of the enum values in the static constructor so this is done only once (this would be a performance improvement).
Another thing, I added an optional parameter so you can ignore the typical "zero" / "None" / "NotApplicable" / "Undefined" / etc value of the enumeration.
I spent some time on searching how to convert Flags enum value to List.
I have found pretty simple solution, maybe it will help someone.
[Flags]
public enum Tag
{
None = 0,
Stablecoin = 1,
NativeTokens = 2,
Dex = 4
}
var values = Tag.Stablecoin | Tag.Dex;
var str = values.ToString(); //"Stablecoin, Dex"
var list = uniqueNftTagsV2.Split(", "); //{"Stablecoin","Dex"}
What if just do something like this:
public static IEnumerable<T> MaskToList<T>(Enum mask)
{
if (typeof(T).IsSubclassOf(typeof(Enum)) == false)
throw new ArgumentException();
List<T> toreturn = new List<T>(100);
foreach(T curValueBit in Enum.GetValues(typeof (T)).Cast<T>())
{
Enum bit = (curValueBit as Enum); // The only difference is actually here,
// use "as", instead of (Enum) cast
if (mask.HasFlag(bit))
toreturn.Add(curValueBit);
}
return toreturn;
}
As the as has not compile time check. Compiler here just "believes" you, hoping that you know what you're doing, so the compile time error not raised.