C# confuse parameter object and enumerator [duplicate] - c#

First, a bit of background. Read the question and accepted answer posted here for a specific scenario for my question. I'm not sure if other, similar cases exist but this is the only case I am aware of.
The above "quirk" is something that I've been aware of for a long time. I didn't understand the full breadth of the cause until just recently.
Microsoft's documentation on the SqlParameter class sheds a little more light on the situation.
When you specify an Object in the value parameter, the SqlDbType is
inferred from the Microsoft .NET Framework type of the Object.
Use caution when you use this overload of the SqlParameter constructor
to specify integer parameter values. Because this overload takes a
value of type Object, you must convert the integral value to an Object
type when the value is zero, as the following C# example demonstrates.
Parameter = new SqlParameter("#pname", Convert.ToInt32(0));
If you do
not perform this conversion, the compiler assumes that you are trying
to call the SqlParameter (string, SqlDbType) constructor overload.
(emph. added)
My question is why does the compiler assume that when you specify a hard coded "0" (and only the value "0") that you are trying to specify an enumeration type, rather than an integer type? In this case, it assumes that you are declaring SqlDbType value, instead of the value 0.
This is non-intuitive and, to make matters worse, the error is inconsistent. I have old applications that I've written which have called stored procedures for years. I'll make a change to the application (often times not even associated with my SQL Server classes), publish an update, and this issue will all of a sudden break the application.
Why is the compiler confused by the value 0, when an object containing multiple method signatures contain two, similar signatures where one parameter is an object/integer and the other accepts an enumeration?
As I've mentioned, I've never seen this as a problem with any other constructor or method on any other class. Is this unique to the SqlParameter class or is this a bug inherit within C#/.Net?

It's because a zero-integer is implicitly convertible to an enum:
enum SqlDbType
{
Zero = 0,
One = 1
}
class TestClass
{
public TestClass(string s, object o)
{ System.Console.WriteLine("{0} => TestClass(object)", s); }
public TestClass(string s, SqlDbType e)
{ System.Console.WriteLine("{0} => TestClass(Enum SqlDbType)", s); }
}
// This is perfectly valid:
SqlDbType valid = 0;
// Whilst this is not:
SqlDbType ohNoYouDont = 1;
var a1 = new TestClass("0", 0);
// 0 => TestClass(Enum SqlDbType)
var a2 = new TestClass("1", 1);
// => 1 => TestClass(object)
(Adapted from Visual C# 2008 Breaking Changes - change 12)
When the compiler performs the overload resolution 0 is an Applicable function member for both the SqlDbType and the object constructors because:
an implicit conversion (Section 6.1) exists from the type of the argument to the type of the corresponding parameter
(Both SqlDbType x = 0 and object x = 0 are valid)
The SqlDbType parameter is better than the object parameter because of the better conversion rules:
If T1 and T2 are the same type, neither conversion is better.
object and SqlDbType are not the same type
If S is T1, C1 is the better conversion.
0 is not an object
If S is T2, C2 is the better conversion.
0 is not a SqlDbType
If an implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists, C1 is the better conversion.
No implicit conversion from object to SqlDbType exists
If an implicit conversion from T2 to T1 exists, and no implicit conversion from T1 to T2 exists, C2 is the better conversion.
An implicit conversion from SqlDbType to object exists, so the SqlDbType is the better conversion
Note that what exactly constitutes a constant 0 has (quite subtly) changed in Visual C# 2008 (Microsoft's implementation of the C# spec) as #Eric explains in his answer.

RichardTowers' answer is excellent, but I thought I'd add a bit to it.
As the other answers have pointed out, the reason for the behaviour is (1) zero is convertible to any enum, and obviously to object, and (2) any enum type is more specific that object, so the method that takes an enum is therefore chosen by overload resolution as the better method. Point two is I hope self-explanatory, but what explains point one?
First off, there is an unfortunate deviation from the specification here. The specification says that any literal zero, that is, the number 0 actually literally appearing in the source code, may be implicitly converted to any enum type. The compiler actually implements that any constant zero may be thusly converted. The reason for that is because of a bug whereby the compiler would sometimes allow constant zeroes and sometimes not, in a strange and inconsistent manner. The easiest way to solve the problem was to consistently allow constant zeroes. You can read about this in detail here:
https://web.archive.org/web/20110308161103/http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx
Second, the reason for allowing zeros to convert to any enum is to ensure that it is always possible to zero out a "flags" enum. Good programming practice is that every "flags" enum have a value "None" which is equal to zero, but that is a guideline, not a requirement. The designers of C# 1.0 thought that it looked strange that you might have to say
for (MyFlags f = (MyFlags)0; ...
to initialize a local. My personal opinion is that this decision has caused more trouble than it was worth, both in terms of the grief over the abovementioned bug and in terms of the oddities it introduces into overload resolution that you have discovered.
Finally, the designers of the constructors could have realized that this would be a problem in the first place, and made the signatures of the overloads such that the developer could clearly decide which ctor to call without having to insert casts. Unfortunately this is a pretty obscure issue and so a lot of designers are unaware of it. Hopefully anyone reading this will not make the same mistake; do not create an ambiguity between object and any enum if you intend the two overrides to have different semantics.

This is apparently a known behavior and affects any function overloads where there is both an enumeration and object type. I don't understand it all, but Eric Lippert summed it up quite nicely on his blog

This is caused by the fact that the integer literal 0 has an implicit conversion to any enum type. The C# specification states:
6.1.3 Implicit enumeration conversions
An implicit enumeration conversion permits the decimal-integer-literal
0 to be converted to any enum-type and to any nullable-type whose
underlying type is an enum-type. In the latter case the conversion is
evaluated by converting to the underlying enum-type and wrapping the
result.
As a result, the most specific overload in this case is SqlParameter(string, DbType).
This does not apply for other int values, so the SqlParameter(string, object) constructor is the most specific.

When resolving the type for an overloaded method, C# selects the most specific option. The SqlParameter class has two constructors that take exactly two arguments, SqlParameter(String, SqlDbType) and SqlParameter(String, Object). When you provide the literal 0, it can be interpreted as an Object or as a SqlDbType. Since SqlDbType is more specific than Object, it is assumed to be the intent.
You can read more about overload resolution in this answer.

Related

Why doesn't the C# compiler consider this generic type inference ambiguous?

Given the following class:
public static class EnumHelper
{
//Overload 1
public static List<string> GetStrings<TEnum>(TEnum value)
{
return EnumHelper<TEnum>.GetStrings(value);
}
//Overload 2
public static List<string> GetStrings<TEnum>(IEnumerable<TEnum> value)
{
return EnumHelper<TEnum>.GetStrings(value);
}
}
What rules are applied to select one of its two generic methods? For example, in the following code:
List<MyEnum> list;
EnumHelper.GetStrings(list);
it ends up calling EnumHelper.GetStrings<List<MyEnum>>(List<MyEnum>) (i.e. Overload 1), even though it seems just as valid to call EnumHelper.GetStrings<MyEnum>(IEnumerable<MyEnum>) (i.e. Overload 2).
For example, if I remove overload 1 entirely, then the call still compiles fine, instead choosing the method marked as overload 2. This seems to make generic type inference kind of dangerous, as it was calling a method which intuitively seems like a worse match. I'm passing a List/Enumerable as the type, which seems very specific and seems like it should match a method with a similar parameter (IEnumerable<TEnum>), but it's choosing the method with the more generic, generic parameter (TEnum value).
What rules are applied to select one of its two generic methods?
The rules in the specification - which are extremely complex, unfortunately. In the ECMA C# 5 standard, the relevant bit starts at section 12.6.4.3 ("better function member").
However, in this case it's relatively simple. Both methods are applicable, with type inference occurring separately for each method:
For method 1, TEnum is inferred to be List<MyEnum>
For method 2, TEnum is inferred to be MyEnum
Next the compiler starts checking the conversions from arguments to parameters, to see whether one conversion is "better" than the other. That goes into section 12.6.4.4 ("better conversion from expression").
At this point we're considering these conversions:
Overload 1: List<MyEnum> to List<MyEnum> (as TEnum is inferred to be List<MyEnum>)
Overload 2: List<MyEnum> to IEnumerable<MyEnum> (as TEnum is inferred to be MyEnum)
Fortunately, the very first rule helps us here:
Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if at least one of the following holds:
E has a type S and an identity conversion exists from S to T1 but not from S to T2
There is an identity conversion from List<MyEnum> to List<MyEnum>, but there isn't an identity conversion from List<MyEnum> to IEnumerable<MyEnum>, therefore the first conversion is better.
There aren't any other conversions to consider, therefore overload 1 is seen as the better function member.
Your argument about "more general" vs "more specific" parameters would be valid if this earlier phase had ended in a tie-break, but it doesn't: "better conversion" for arguments to parameters is considered before "more specific parameters".
In general, both overload resolution is incredibly complicated. It has to take into account inheritance, generics, type-less arguments (e.g. the null literal, the default literal, anonymous functions), parameter arrays, all the possible conversions. Almost any time a new feature is added to C#, it affects overload resolution :(

C# overload is puzzled [duplicate]

today I discovered a very strange behavior with C# function overloading. The problem occurs when I have a method with 2 overloads, one accepting Object and the other accepting Enum of any type. When I pass 0 as parameter, the Enum version of the method is called. When I use any other integer value, the Object version is called. I know this can be easilly fixed by using explicit casting, but I want to know why the compiler behaves that way. Is this a bug or just some strange language rule I don't know about?
The code below explains the problem (checked with runtime 2.0.50727)
Thanks for any help on this,
Grzegorz Kyc
class Program
{
enum Bar
{
Value1,
Value2,
Value3
}
static void Main(string[] args)
{
Foo(0);
Foo(1);
Console.ReadLine();
}
static void Foo(object a)
{
Console.WriteLine("object");
}
static void Foo(Bar a)
{
Console.WriteLine("enum");
}
}
It may be that you're not aware that there's an implicit conversion from a constant1 of 0 to any enum:
Bar x = 0; // Implicit conversion
Now, the conversion from 0 to Bar is more specific than the conversion from 0 to object, which is why the Foo(Bar) overload is used.
Does that clear everything up?
1 There's actually a bug in the Microsoft C# compiler which lets it be any zero constant, not just an integer:
const decimal DecimalZero = 0.0m;
...
Bar x = DecimalZero;
It's unlikely that this will ever be fixed, as it could break existing working code. I believe Eric Lippert has a two blog posts which go into much more detail.
The C# specification section 6.1.3 (C# 4 spec) has this to say about it:
An implicit enumeration conversion
permits the decimal-integer-literal 0
to be converted to any enum-type and
to any nullable-type whose underlying
type is an enum-type. In the latter
case the conversion is evaluated by
converting to the underlying enum-type
and wrapping the result (§4.1.10).
That actually suggests that the bug isn't just in allowing the wrong type, but allowing any constant 0 value to be converted rather than only the literal value 0.
EDIT: It looks like the "constant" part was partially introduced in the C# 3 compiler. Previously it was some constant values, now it looks like it's all of them.
I know I have read somewhere else that the .NET system always treats zero as a valid enumeration value, even if it actually isn't. I will try to find some reference for this...
OK, well I found this, which quotes the following and attributes it to Eric Gunnerson:
Enums in C# do dual purpose. They are used for the usual enum use, and they're also used for bit fields. When I'm dealing with bit fields, you often want to AND a value with the bit field and check if it's true.
Our initial rules meant that you had to write:
if ((myVar & MyEnumName.ColorRed) != (MyEnumName) 0)
which we thought was difficult to read. One alernative was to define a zero entry:
if ((myVar & MyEnumName.ColorRed) != MyEnumName.NoBitsSet)
which was also ugly.
We therefore decided to relax our rules a bit, and permit an implicit conversion from the literal zero to any enum type, which allows you to write:
if ((myVar & MyEnumName.ColorRed) != 0)
which is why PlayingCard(0, 0) works.
So it appears that the whole reason behind this was to simply allow equating to zero when checking flags without having to cast the zero.

Console.WriteLine(Enum.Value) gives different output in C# and VB.Net

I am basically a C# guy, but writing VB.Net code these days.
Today I came across a very different behaviour of .Net
C# Code
enum Color
{
Red,
Green,
Blue
}
class Demo
{
public static void Main()
{
System.Console.WriteLine(Color.Red);
}
}
This prints Red
But when this code is written in VB.Net it prints 0.
VB.Net Code
Module Module1
Sub Main()
System.Console.WriteLine(Color.Red)
End Sub
End Module
Enum Color
Red
Green
Blue
End Enum
Why so different?
There is no Console.WriteLine(Enum) overload so the compilers are forced to pick one of the other ones. Overload resolution rules are very arcane and the VB.NET and C# rules are not the same, but both compilers are willing to pick one when there's an implicit conversion to the target argument type and pick the one that takes the least amount of work.
Which is where another rule applies, this kind of statement in VB.NET is perfectly valid:
Dim example As Integer = Color.Red '' Fine
But the C# compiler spits at:
int example = Color.Red; // CS0266
Insisting that you apply an (int) cast. It only has an explicit conversion, not an implicit one like VB.NET.
So the C# compiler is going to ignore all the overloads that take an integral argument, none are candidates because only explicit conversions exist for them. Except one, the Console.WriteLine(Object) overload. There is an implicit conversion for that one, it takes a boxing conversion.
The VB.NET compiler sees it as well, but now the "better" conversion comes into play. A boxing conversion is a very expensive conversion, converting to Integer is very cheap. It requires no extra code. So it likes that one better.
Workarounds are simple:
System.Console.WriteLine(CObj(Color.Red)) '' or
System.Console.WriteLine(Color.Red.ToString())
C# and VB.NET have different method overload resolution rules.
C# Picks Console.WriteLine(Object), while VB.NET picks Console.WriteLine(Int32). Let's see why it does so.
VB.NET rules:
Accessibility. It eliminates any overload with an access level that prevents the calling code from calling it.
Number of Parameters. It eliminates any overload that defines a different number of parameters than are supplied in the call.
Parameter Data Types. The compiler gives instance methods preference over extension methods. If any instance method is found that requires only widening conversions to match the procedure call, all extension methods are dropped and the compiler continues with only the instance method candidates. If no such instance method is found, it continues with both instance and extension methods.
In this step, it eliminates any overload for which the data types of the calling arguments cannot be converted to the parameter types defined in the overload.
Narrowing Conversions. It eliminates any overload that requires a narrowing conversion from the calling argument types to the defined parameter types. This is true whether the type checking switch (Option Strict Statement) is On or Off.
Least Widening. The compiler considers the remaining overloads in pairs. For each pair, it compares the data types of the defined parameters. If the types in one of the overloads all widen to the corresponding types in the other, the compiler eliminates the latter. That is, it retains the overload that requires the least amount of widening.
Single Candidate. It continues considering overloads in pairs until only one overload remains, and it resolves the call to that overload. If the compiler cannot reduce the overloads to a single candidate, it generates an error.
There are a lot of overloads for WriteLine, some of them are discarded at step 3. We're basically left with the following possibilities: Object and the numeric types.
The 5th point is interesting here: Least Widening. So what do the widening rules say?
Any enumerated type (Enum) widens to its underlying integral type and any type to which the underlying type widens.
Any type widens to Object
So, your Color enum first widens to Int32 (its underlying data type) - and this is a 100% match for Console.WriteLine(Int32). It would require yet another widening conversion to go from Int32 to Object, but the rules above say to retain the overload that requires the least amount of widening.
As for C# (from the C# 5 spec at §7.5.3.2):
Given an argument list A with a set of argument expressions { E1, E2, ..., EN } and two applicable function members MP and MQ with parameter types { P1, P2, ..., PN } and { Q1, Q2, ..., QN }, MP is defined to be a better function member than MQ if
for each argument, the implicit conversion from EX to QX is not better than the implicit conversion from EX to PX, and
for at least one argument, the conversion from EX to PX is better than the conversion from EX to QX.
Ok, now how is better defined (§7.5.3.4)?
Given a conversion C1 that converts from a type S to a type T1, and a conversion C2 that converts from a type S to a type T2, C1 is a better conversion than C2 if at least one of the following holds:
An identity conversion exists from S to T1 but not from S to T2
T1 is a better conversion target than T2 (§7.5.3.5)
Let's see at §7.5.3.5:
Given two different types T1 and T2, T1 is a better conversion target than T2 if at least one of the following holds:
An implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists
T1 is a signed integral type and T2 is an unsigned integral type.
So, we're converting from Color to either Object or Int32. Which one is better according to these rules?
There is an implicit conversion from Color to Object
There is no implicit conversion from Object to Color (obviously)
There is no implicit conversion from Color to Int32 (these are explicit in C#)
There is no implicit conversion from Int32 to Color (except for 0)
Spec §6.1:
The following conversions are classified as implicit conversions:
Identity conversions
Implicit numeric conversions
Implicit enumeration conversions.
Implicit nullable conversions
Null literal conversions
Implicit reference conversions
Boxing conversions
Implicit dynamic conversions
Implicit constant expression conversions
User-defined implicit conversions
Anonymous function conversions
Method group conversions
Implicit numeric conversions make no mention of enum types, and Implicit enumeration conversions deal with the other way around:
An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any enum-type and to any nullable-type whose underlying type is an enum-type. In the latter case the conversion is evaluated by converting to the underlying enum-type and wrapping the result (§4.1.10).
Enums are handled by boxing conversions (§6.1.7):
A boxing conversion permits a value-type to be implicitly converted to a reference type. A boxing conversion exists from any non-nullable-value-type to object and dynamic, to System.ValueType and to any interface-type implemented by the non-nullable-value-type. Furthermore an enum-type can be converted to the type System.Enum.

Is there a technical reason for no implicit conversion from DBNull to nullable types?

Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
Is there a technical reason why there is no implicit conversion from DBNull to the various nullable and/or sql types? I understand why the conversions don't currently happen, but don't understand why an implicit conversion wasn't created at the time or added in subsequent versions of the framework.
Just to be clear, I'm looking for technical reasons, not "because that's they way they did it" or "I like it that way".
The most straightforward explanation comes from Microsoft's documentation:
CLR nullable types are not intended for storage of database nulls because an ANSI SQL null does not behave the same way as a null reference (or Nothing in Visual Basic).
Well, I don't know about the SqlTypes case, but there definitely are some technical reasons why adding an implicit conversion between DBNull.Value and values of Nullable<T> with HasValue = false wouldn't work.
Remember, DBNull is a reference type, and despite the fact that Nullable<T> acts like a reference type -- by pretending to be able to take on the null value -- it's actually a value type, with value semantics.
In particular, there's a weird edge case when values of type Nullable<T> are boxed. The behavior is special-cased in the runtime to box values of type Nullable<T> to a boxed version of T, not a boxed version of Nullable<T>.
As the MSDN documentation explains it:
When a nullable type is boxed, the common language runtime automatically boxes the underlying value of the Nullable(Of T) object, not the Nullable(Of T) object itself. That is, if the HasValue property is true, the contents of the Value property is boxed. When the underlying value of a nullable type is unboxed, the common language runtime creates a new Nullable(Of T) structure initialized to the underlying value.
If the HasValue property of a nullable type is false, the result of a boxing operation is Nothing. Consequently, if a boxed nullable type is passed to a method that expects an object argument, that method must be prepared to handle the case where the argument is Nothing. When Nothing is unboxed into a nullable type, the common language runtime creates a new Nullable(Of T) structure and initializes its HasValue property to false.
Now we get into a tricky problem: the C# language spec (§4.3.2) says we can't use an unboxing conversion to convert DBNull.Value into Nullable<T>:
For an unboxing conversion to a given nullable-type to succeed at run-time, the value of the source operand must be either null or a reference to a boxed value of the underlying non-nullable-value-type of the nullable-type. If the source operand is a reference to an incompatible object, a System.InvalidCastException is thrown.
And we can't use a user-defined conversion to convert from object to Nullable<T>, either, according to §10.10.3:
It is not possible to directly redefine a pre-defined conversion. Thus, conversion operators are not allowed to convert from or to object because implicit and explicit conversions already exist between object and all other types.
OK, you or I couldn't do it, but Microsoft could just amend the spec, and make it legal, right? I don't think so.
Why? Well, imagine the intended use case: you've got some method that is specified to return object. In practice, it either returns DBNull.Value or int. But how could the compiler know that? All it knows is that the method is specified to return object. And the conversion operator to be applied must be selected at compile time.
OK, so assume that there is some magical operator that can convert from object to Nullable<T>, and the compiler has some way of knowing when it is applicable. (We don't want it to used for every method that is specified to return object -- what should it do if the method actually returns a string?) But we still have an issue: the conversion could be ambiguous! If the method returns either long, or DBNull.Value, and we do int? v = Method();, what should we do when the method returns a boxed long?
Basically, to make this work as intended, you'd have to use the equivalent of dynamic to determine the type at runtime and convert based on the runtime type. But then we've broken another rule: since the actual conversion would only be selected at runtime, there's no guarantee that it would actually succeed. But implicit conversions are not supposed to throw exceptions.
So at this point, it's not only a change to the specified behavior of the language, a potentially significant performance hit, and on top of that it could throw an unexpected exception! That seems like a pretty good reason not to implement it. But if you need one more reason, remember that every feature starts out minus 100 points.
In short: what you really want here can't be done with an implicit conversion anyway. If you want the behavior of dynamic, just use dynamic! This does what you want, and is already implemented in C# 4.0:
object x = 23;
object y = null;
dynamic dx = x;
dynamic dy = y;
int? nx = (int?) dx;
int? ny = (int?) dy;
Console.WriteLine("nx.HasValue = {0}; nx.Value = {1}; ny.HasValue = {2};",
nx.HasValue, nx.Value, ny.HasValue);

Strange (possibly wrong?) C# compiler behavior with method overloading and enums

today I discovered a very strange behavior with C# function overloading. The problem occurs when I have a method with 2 overloads, one accepting Object and the other accepting Enum of any type. When I pass 0 as parameter, the Enum version of the method is called. When I use any other integer value, the Object version is called. I know this can be easilly fixed by using explicit casting, but I want to know why the compiler behaves that way. Is this a bug or just some strange language rule I don't know about?
The code below explains the problem (checked with runtime 2.0.50727)
Thanks for any help on this,
Grzegorz Kyc
class Program
{
enum Bar
{
Value1,
Value2,
Value3
}
static void Main(string[] args)
{
Foo(0);
Foo(1);
Console.ReadLine();
}
static void Foo(object a)
{
Console.WriteLine("object");
}
static void Foo(Bar a)
{
Console.WriteLine("enum");
}
}
It may be that you're not aware that there's an implicit conversion from a constant1 of 0 to any enum:
Bar x = 0; // Implicit conversion
Now, the conversion from 0 to Bar is more specific than the conversion from 0 to object, which is why the Foo(Bar) overload is used.
Does that clear everything up?
1 There's actually a bug in the Microsoft C# compiler which lets it be any zero constant, not just an integer:
const decimal DecimalZero = 0.0m;
...
Bar x = DecimalZero;
It's unlikely that this will ever be fixed, as it could break existing working code. I believe Eric Lippert has a two blog posts which go into much more detail.
The C# specification section 6.1.3 (C# 4 spec) has this to say about it:
An implicit enumeration conversion
permits the decimal-integer-literal 0
to be converted to any enum-type and
to any nullable-type whose underlying
type is an enum-type. In the latter
case the conversion is evaluated by
converting to the underlying enum-type
and wrapping the result (§4.1.10).
That actually suggests that the bug isn't just in allowing the wrong type, but allowing any constant 0 value to be converted rather than only the literal value 0.
EDIT: It looks like the "constant" part was partially introduced in the C# 3 compiler. Previously it was some constant values, now it looks like it's all of them.
I know I have read somewhere else that the .NET system always treats zero as a valid enumeration value, even if it actually isn't. I will try to find some reference for this...
OK, well I found this, which quotes the following and attributes it to Eric Gunnerson:
Enums in C# do dual purpose. They are used for the usual enum use, and they're also used for bit fields. When I'm dealing with bit fields, you often want to AND a value with the bit field and check if it's true.
Our initial rules meant that you had to write:
if ((myVar & MyEnumName.ColorRed) != (MyEnumName) 0)
which we thought was difficult to read. One alernative was to define a zero entry:
if ((myVar & MyEnumName.ColorRed) != MyEnumName.NoBitsSet)
which was also ugly.
We therefore decided to relax our rules a bit, and permit an implicit conversion from the literal zero to any enum type, which allows you to write:
if ((myVar & MyEnumName.ColorRed) != 0)
which is why PlayingCard(0, 0) works.
So it appears that the whole reason behind this was to simply allow equating to zero when checking flags without having to cast the zero.

Categories