Repository navigation
Proposal: "static cast" operator for C# #1203
Description
Activity
- The idea makes sense, but is there a more realistic example? Those overloads do not look like good anyway.
- The suggested syntax looks too much like TypeScript IMHO - could be confusing with the upcoming record type syntax as well.
I think the biggest problem with the syntax is that it's ambiguous with named parameters. Does
M(color : Color)mean "the value of the parameter namedcolorisColor" or "the value of the first parameter iscolor, statically cast to the typeColor"?Most intuitive would be saying 'as' or 'like':
M(x like IList<int>);
How about:
M(IList<int>(x));- Reacted by Viacheslav Ivanov
@dsaf But then you have to add extra syntax rules to the parser which could cause ambiguity down the line (cant think of anything off the top of my head, but you never know).
@ViIvanov, I'm trying very hard to understand the value of this proposal.
Other than the way you write the cast, is there any thing new? Any motivation?
@paulomorgado, did you read the whole of X2 body? It's not a feature suggestion, it's a fix to existing language design.
@dsaf, a fix is supposed to be a solution to something that is broken.
I'm failing to understand what it is broken and what the solution is.
@paulomorgado, did you read the X2 method carefully? Why is it necessary to use an unsafe cast to resolve the method ambiguity? It can lead to bugs as the code changes.
Related reading:
http://en.wikipedia.org/wiki/Type_conversion
http://en.wikipedia.org/wiki/Function_overloading
https://msdn.microsoft.com/en-us/library/aa691336%28v=vs.71%29.aspx
@dsaf , thank for clarifying that the problem is in my reading and not on the writting.
So, if I understand it correctly, the problem is that the cast (
(IList<int>)x) might fail at run time, but you want the "conversion", not the "conversion if" (x as IList<int>).The proposal is to write this:
void X(Collection<int> x) { IList<int> y = x; M(y); }in a more succint way, like many other C# features.
If declaration expressions are introduced, we will be able to write that as:
void X(Collection<int> x) { M(IList<int> y = x); }That poses an interesting problem regarding syntax. I wonder if the compiler could handle
M(x: x : IList<int>).For the present exemples, naming the parameters diferently would also be a solution:
void M(IList<int> l) { } // 1 void M(IReadOnlyList<int> rl) { } // 2 void M(IList<string> l) { } // 3 void M(IReadOnlyList<string> rl) { } // 4 void X(Collection<int> x) { M(l: x); }But that's still working around the problem, not solving it.
I'm sorry for a late answer.
- OK, I will find real examples from my code base. May be code below?
- I'm OK with other syntax of this operator. Yes, with named arguments colon can be hard.
@paulomorgado
You are right, in a this case a strongly typed local variable now (without static cast operator) is better. But, what about this example in a linq query?var items = from item in GetItems() where SomeFilter((IList<int>)item.Fields) select item;
We can not use explicitly typed local variable here.
A joke I use a next code for this:
var items = from item in GetItems() where SomeFilter(item.Fields ?? default(IList<int>)) select item;
It's work, but not clear.
2 remaining items
@paulomorgado
Main concern of static cast operator is tell that we do not need any special conversion operations. We just need to clarify a static type of the expression.I think, it makes a reading a code simpler, intentions of author of code more explicit.
Local variables is not a good because
- Author should invent a good names for these variables.
- Refactoring of code with it variables is harder
- Readers should know - why author introduces local variable. I think it is not absolutely clear.
If "x" is not a simple expression
M(IList<int>(x1 + x2.Mmm(sss)));
the result IMHO is a not good. I think
M((x1 + x2.Mmm(sss)) :> IList<int>);
better, when expression and the proposed type of expression visually separated from each other, peer to peer.
I doubt that C# team would be open to introducing a new operator (
:>) for a relatively small problem like this. Even for pattern matching proposal they tried to re-use the switch statement as much as possible.Is it inspired by F#'s one? https://msdn.microsoft.com/en-us/library/dd233220.aspx
It feels a bit alien to C#.@dsaf
Agree. It just purposes. I think a:is a better and code like nextclass x { } class y : x { } static void M(x x) { } static void M(y x) { } static void Main() { var x = new y(); // What does it mean? // First "x" for the name of a parameter // Second "x" for the argument // or // First "x" for for the argument // Second "x" for the explicit type M(x : x); // First "x" for the name of a parameter // Second "x" for the argument // Third "x" for the explicit type M(x : x : x); }
can be resolved by a compiler. I think it's clear it mean a named parameter or it can be a compiler error.
I really want a "safe upcast" operator like this.
Here's another use-case that comes up a lot for me:
public class C { public C(params int[] values) : this((IEnumerable<int>)values) { } public C(IEnumerable<int> r) { // real constructor } }(Of course, this could also be solved by enumerable
params, but...)It annoys me that I have to use the sledgehammer cast operator when I just want a compiler-proven-safe upcast to the base type.
Not overly found of the syntax. But the best syntax (
as) has already been stolen...safeas?as2?asEx? 😄Using "attributes everywhere" (https://github.com/dotnet/roslyn/issues/6671) this can be implemented as an analyzer,
[SourceAttributeUsage(SourceAttributeTargets.Cast)] class StaticCastAttribute {} M([StaticCast](IList<int>)x);
I'm sorry, perhaps I'm pedantic: what is wrong with
as?@whoisj It allows you to do a "down cast" while OP wants to do just a "up cast".
Reacted by J Wyman@whoisj It allows you to do a "down cast" while OP wants to do just a "up cast".
Ahh yes - thanks for that. I think I've been thinking C# too long now and I don't even attempt to "up cast" any more. 😏
We are now taking language feature discussion in other repositories:
- https://github.com/dotnet/csharplang for C# specific issues
- https://github.com/dotnet/vblang for VB-specific features
- https://github.com/dotnet/csharplang for features that affect both languages
Features that are under active design or development, or which are "championed" by someone on the language design team, have already been moved either as issues or as checked-in design documents. For example, the proposal in this repo "Proposal: Partial interface implementation a.k.a. Traits" (issue 16139 and a few other issues that request the same thing) are now tracked by the language team at issue 52 in https://github.com/dotnet/csharplang/issues, and there is a draft spec at https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md and further discussion at issue 288 in https://github.com/dotnet/csharplang/issues. Prototyping of the compiler portion of language features is still tracked here; see, for example, https://github.com/dotnet/roslyn/tree/features/DefaultInterfaceImplementation and issue 17952.
In order to facilitate that transition, we have started closing language design discussions from the roslyn repo with a note briefly explaining why. When we are aware of an existing discussion for the feature already in the new repo, we are adding a link to that. But we're not adding new issues to the new repos for existing discussions in this repo that the language design team does not currently envision taking on. Our intent is to eventually close the language design issues in the Roslyn repo and encourage discussion in one of the new repos instead.
Our intent is not to shut down discussion on language design - you can still continue discussion on the closed issues if you want - but rather we would like to encourage people to move discussion to where we are more likely to be paying attention (the new repo), or to abandon discussions that are no longer of interest to you.
If you happen to notice that one of the closed issues has a relevant issue in the new repo, and we have not added a link to the new issue, we would appreciate you providing a link from the old to the new discussion. That way people who are still interested in the discussion can start paying attention to the new issue.
Also, we'd welcome any ideas you might have on how we could better manage the transition. Comments and discussion about closing and/or moving issues should be directed to #18002. Comments and discussion about this issue can take place here or on an issue in the relevant repo.
I am not moving this particular issue because I don't have confidence that the LDM would likely consider doing this because it can be done in an API.
public static T StaticCast<T>(T value) => value;
Reacted by Alireza Habibi, J Wyman and Viacheslav Ivanov@gafter Thanks. In case I will found some other arguments for this operator I will open new discussion in dotnet/csharplang repo.
- added a commit that references this issue
on Aug 18, 2026 - added a commit that references this issue
on Aug 18, 2026
It would be nice to have a "static cast" operator in C#.
Existing type cast operator looks similar in cases, when it throws InvalidCastException (or other from user defined type cast operator, for example) and when it not throws (converting to a base type or to an interface or enum type to an underlying type).
I.e. "static cast" is the cast that can be produced without exceptions.
Example: