Proposal: Allow 'as' operator to be used with non-nullable value types #1641
-
Currently C# has two ways of converting between types outside of selection statements in the language - casts and the Current Optionsint i = (int) await _service.GetIntAsObject();
int j = await _service.GetIntAsObject() as int; Some find the second option to be more aesthetically pleasing/easier to read and try to use it where it makes sense. However, the language currently forbids the int i = await _service.GetIntAsObject() as int? ?? 0; Which not only can be a little confusing at first sight but, more importantly, doesn't really convey the meaning desired by the developer which is to say that the method will certainly return an int. If the method is not guaranteed to return a non-null value, the developer might then write something like: int i = await _service.GetIntAsObject() as int? ?? throw new NullReferenceException(); However, this not only is a bit long, but this code also throws a int i = await _service.GetIntAsObject() as int? ?? throw new InvalidCastException(); This also throws an exception which is more or less reserved for the framework, but it is [arguably] more acceptable. It stills suffers from the problem of being too long and cumbersome to write. It also litters the code with unnecessary logic: both these last two examples write logic that should be reserved to the framework. How the new operator could workNow, if we were to allow Use
|
Beta Was this translation helpful? Give feedback.
Replies: 93 comments
-
IMO the existing cast syntax is the most concise and readable way of conveying that you want to cast something. I think trying to extend the |
Beta Was this translation helpful? Give feedback.
-
Hmm, I'm sorry but I disagree. I don't see how my options for behavior demonstrates that. Could you elaborate?
The very point of this proposal is that different developers prefer different coding styles. Also, the point is not simply casting, but converting in general. We could use that same argument to remove the |
Beta Was this translation helpful? Give feedback.
-
I certainly can see that this is a style you prefer... but i have no information that would make me feel this is any way something enough of the community would want to warrant doing this. It would be equivalent to someone coming here and saying: "i would prefer to say |
Beta Was this translation helpful? Give feedback.
-
Well, you're having to come up with all sorts of decisions about what The virtue of 'is'/'as' today is that they do mean different things. |
Beta Was this translation helpful? Give feedback.
-
The fact that there are multiple options for what this operator should do if it fails is evidence that it is confusing. The The current rules seem to be quite sufficient, and they allow the developer to decide the policy on failed casts: int withDefaultValue = expression as int? ?? default;
int withAlternateValue = expression as int? ?? -1;
int withException = expression as int? ?? throw new Exception("boom");
Indeed they do, but having the language enshrine that through duplication of syntax is generally not considered a good thing unless that alternate syntax provides sufficient additional benefit. |
Beta Was this translation helpful? Give feedback.
-
Can you point to such communities?
Can you point to such codebases? It seems odd for a codebase to have a preference here as these are two totally different things. it's not like |
Beta Was this translation helpful? Give feedback.
-
That is why we have reactions and an open discussion forum.
Well, and I guess it's totally OK to propose that as well (not that the community would like it though 😄).
No, not "you". Stop thinking about me. Think about the proposal. Think about the community, not the OP. This is open-source.
Of course! I mean, when you're proposing to change something in the language, of course you'll have to come up with decisions as to how it will behave. (?!)
I think this is a very valid point. My main reason is to bring consistency -- to be able to use the same construct everywhere it seems to make sense. |
Beta Was this translation helpful? Give feedback.
-
Yes, and that is why I proposed the third way, which - I think - would marry well with the new nullable reference types feature. In C# 8 (possibly), writing |
Beta Was this translation helpful? Give feedback.
-
That's fair. But it's worth pointing out that i think the language already has that. You can do |
Beta Was this translation helpful? Give feedback.
-
Note that some of these things 'fell out' of other full features added to the language. i.e. the intent was not to add "another way to check for null". INstead, "pattern matching" was added. But patterns wanted to check for null (a very common thing to do in a pattern), and so we ended up with where we are now. That's a very different situation than this proposal. I'm ok with having very large scale features that end up makign it so there are occasionally multiple ways to do things. I'm not a huge fan with a feature that exists solely and specifically to duplicate something that can already be done :) (does that help clarify things). |
Beta Was this translation helpful? Give feedback.
-
Well... it was your claim that people want this and that codebases follow these styles. I'm just asking for clarification :) |
Beta Was this translation helpful? Give feedback.
-
I agree. It's just that currently the only way to tell the compiler "I know this will have an int; it won't be null, trust me" is using a cast. Which is, of course, a totally valid and sensible way to do it. However, if you, like me, thinks that
Yep, thanks.
Nope, I never claimed that. I'll quote myself here:
"Some", I said. Like: me, some couple people on SO (https://stackoverflow.com/questions/24265139/why-can-the-as-operator-not-be-used-to-parse-non-nullable-value-types), some fellows here in the company, whoever upvotes this issue...
Note I didn't say any codebase follow this style. I said that in codebases that follows it... Not that there are codebases that follow it - just that in the ones which do follow.... So no, I never claimed any of these things you said I claimed. Anyway, inb4: yes, we do follow this style in one of the projects here in my company. Yes, we do know casts and |
Beta Was this translation helpful? Give feedback.
-
Sorry... i'm confused by this. You said "in codebases that follows it". Are there codebases that follow this? If so, can you point me to them so i can look at them? If there are, great, it will be helpful to see and try to understand what's going on. If there aren't... then i'm not sure why it's relevant to bring up "in codebases that follows it"
It's not about micro-optimizations. it's about using the relevant construct to convey the concept you're trying to convey. cast/as are two entirely different concepts. One is "do this, and fail otherwise" the other is "try this, and give me a sentinel otherwise". It's not about a micro-opt, it's about what the failure behavior should be and what makes sense in that domain :) |
Beta Was this translation helpful? Give feedback.
-
I think this an excellent proposal. I like the shape of |
Beta Was this translation helpful? Give feedback.
-
It's not supposed to be used for all casts. It's to be used specifically for fallible casts, and to that end requires a sensible default value for when the cast fails. That doesn't exist with value types. Liking the appearance or the flow doesn't address any the problems with it. |
Beta Was this translation helpful? Give feedback.
-
My thinking goes like this:
However, I accept that point 1 is flawed as the two are not equivalent as the rules around casting are complex and there are differences between the two. And I accept that Thankfully, I'd still like to be able to use |
Beta Was this translation helpful? Give feedback.
-
What? You're basically claiming that the following code:
Is equivalent to the following code:
But the IL is very different so I'm not sure how you came to this conclusion. |
Beta Was this translation helpful? Give feedback.
-
I wasn't fan of this proposal at first, but the more I think about it the more I believe the mental swap @DavidArno mentioned is usefull:
If zero is part of your domain, then too bad and you shouldn't use |
Beta Was this translation helpful? Give feedback.
-
@zippec
There's a major difference between null that you expect to one that don't, in a case of failure the latter will throw an exception whereas with zero you might never even know about it, you go from "safer" to "less safer". What's funny is that some people are avid advocates of the famous mantra "falling into the pit of success" only when it suits them. 😄 |
Beta Was this translation helpful? Give feedback.
-
They are functionally equivalent. I don't see the relevance of how they are implemented in IL. If I can swap one version for the other in code and have no unit tests fail, then they are equivalent. I believe there are edge cases I think where they differ, but I mentioned that caveat already. |
Beta Was this translation helpful? Give feedback.
-
Well, I mentioned the IL part only because you wrote it swallows exceptions. Personally, I don't have a strong opinion about this change so I removed my downvote and completely understand why people would like it although I'm more in favour of something like |
Beta Was this translation helpful? Give feedback.
-
"it swallows exceptions" should not be taken literally. "It doesn't throw an exception when the cast fails" is likely a more accurate description of what I was trying to say. Isn't |
Beta Was this translation helpful? Give feedback.
-
I thought so too, I was quite surprised to see the idea being revived. IMHO it wouldn't be so bad if The other way to look at it is |
Beta Was this translation helpful? Give feedback.
-
I dunno I just like the idea of a general feature where you can convert from one thing to another and it would also cover the cases discussed here, the only downside to this is that the BCL would have to adapt this feature but the advantage is that you have full control over it and can add different kinds of conversions without thinking about the semantics of casting so you get the benefit of Anyway, I don't want to derail the discussion so if you feel like I'm doing it please, tell me and I'll stop. |
Beta Was this translation helpful? Give feedback.
-
Possibly, but that would be the same behavior as |
Beta Was this translation helpful? Give feedback.
-
Definitely agree with @HaloFour there. Having |
Beta Was this translation helpful? Give feedback.
-
What about introducing a cast keyword? |
Beta Was this translation helpful? Give feedback.
-
@gdalsnes I don't think there's any indicated interest in adding new syntax that serves the exact purpose of a normal |
Beta Was this translation helpful? Give feedback.
Well, you're having to come up with all sorts of decisions about what
as valuetype
means. One of those decisions is to just make it mean the same thing that(valuetype)
means. So why have two identical ways to say the exact same thing?The virtue of 'is'/'as' today is that they do mean different things.
(type)
is "convert to this type, and throw if you can't".as type
is "convert to this type and give me null if you can't". Unless your proposal substantively gives me something different from those two options, then it's not really something valuable to add to the language.