New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inconsistent/ambigious C# 'default' operator behavior with value types #2125
Comments
This is all as per the C# language specification. Importantly, changing any of this would be a breaking change in behavior. If this is something you don't like, you can always write a Roslyn analyzer to check your code and identify these places for you. |
Let's pretend that is true. Here is an example of why it would be a bad idea. If you extract the expression as a variable, then the result of that expression would change:
There is also the issue of Furthermore, what about implicit conversions.
Since there is an implicit conversion from |
I think you donot get the idea here. Before just saying Moreover, the line you put is ambiguous. Even to you, it is ambigious. Read your words, you will see what am saying. Cherry on top, this is exclusively for value types. It is bad for readability (God forbid somebody read the code and didnot notice that you using a value type) and it is not clear at all when it is a value type. |
Personally I think you should be using Why does more characters than necessary? |
Also, this is a intentionally bad example. Guid? z = (y == default) ? y : default; Reduces to Guid? z = (y == default) ? default : default; And that reduces to Guid? z = default(Guid); And now the code isn't confusing at all. |
@CyrusNajmabadi here are a couple of points for you:
|
This seems misleading. Guid? z = (y == default) ? y : default; This is effectively, assuming Guid? z = (y == default(typeof(y))) ? y : default(target-type); Which is then: Guid? z = (y == default(Guid)) ? y : default(target-type); This can then be simplified to: Guid? z = (y == default(Guid)) ? default(Guid) : default(target-type); However, the target type of the entire expression is
If you're using Visual Studio, the hover tooltip will tell you the exact type being inferred, in exactly the same manner as |
You are giving a plausible example which I find unrealistic at all. The piece of code I originally posted comming out of an actual program that is in production. Another point worth mentioning (@CyrusNajmabadi @Grauenwolf), very ironic one, if you wrote |
@yaakov-h Visual Studio is not a part of the C# language. It is an IDE, simply, an text editor tool. You can not just say, well, look at the tooltip! |
It's not ambiguous. The language defines exactly what it means. And, as i mentioned above, changing this would be a breaking change. it will not happen. If you do not like this, you can write your own analyzer that prevents you from writing this. |
The issue you have has nothing explicitly to do with value types but instead with implicit type casting, order of operations, and null semantics. @Grauenwolf hit on this, but I wanted to expand on what exactly is going on in the line that you wrote that causes it to assume that Given the code below: Guid y = Guid.NewGuid();
Guid? z = (y == default) ? y : default; The z assignment line is broken up into a couple of different sub-expressions. These expressions are:
Expr1 is a Expr2 is a Expr3 is an assignment operator that implicitly converts the type of Expr2 to the defined type -- If we were to rewrite your code out using explicit casts where the implicit casts are occuring would look like this: Guid y = Guid.NewGuid();
Guid? z = (Guid?) ((y == default(Guid)) ? y : default(Guid))); Based on all of this, it is my opinion that this isn't an issue with the language itself. This type of ambiguity is inherent with implicit conversions and is the reason why you must be aware of what you're writing when doing implicit casting. Stylistically, I think it is better to do one of these two: Guid y = Guid.NewGuid();
Guid? z = default;
if (y == default)
z = y; or (given that I like to use anonymous blocks to group and set aside complex variable initialization) Guid y = Guid.NewGuid();
Guid? z;
{
if (y == default)
z = y;
else
z = default;
} |
Yup. And any time any sort of inference/whatever is used, you need to go figure out what it means. This is not new to this feature. This is inherent to C#, and has been since 3.0.
You can't. It's a breaking change it will not happen.
This is not your call. You've raised a concern. But it's how the language works, and is certainly in use by code out there. The behavior cannot be changed. If you don't like this behavior, you are welcome to write your own analyzer that prevents you from using this language feature. But the language will not be changed to have this meaning now be different from one version of the language to another. |
Yes you can. Because that's how it works for so much of the language. You can't tell just from looking at text what That's the direction the language decided to go in. If you don't like that, there are simple solutions. Just don't use those features, and/or create analyzers to prevent their use in your codebase. |
That is not true. The IDE understands this: However, if you write: Then the IDE correctly determines that this can be simplified to: |
@SherifRefaat I would recommend taking the discussion about breaking changes to gitter.im/dotnet/csharplang. It's going to be a non-starter here. |
@CyrusNajmabadi Okay. Thanks for your time. |
Hi, I am not entirely sure about this. But personally, I don't think this is ever right in terms of secure coding.
Here it is:
Guid? x = default(Guid?); this is equivalent to Guid? x = default;
which produce
null
(it is fine).Let's have the following
Guid y = Guid.NewGuid();
then
Guid? z = (y == default) ? y : default(Guid?);
this will make
z
equal tonull
. Which again is valid. But if you doGuid? z = (y == default) ? y : default;
this will make
z
equal to00000000-0000-0000-0000-000000000000
(default value ofSystem.Guid
)Implicitly, you mean here the
default
forz
noty
.I think this is a problem in terms of secure coding since
z == null
will always fail since it is equal to00000000-0000-0000-0000-000000000000
More worse (in my opinion), this
z == default
will always evaluate to false since it contains the default value ofGuid
notGuid?
By the way, it seems that this behavior is consistent with all
T?
orNullable<T>
where T is a value type.Thanks.
The text was updated successfully, but these errors were encountered: