You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@uxmal Short answer - with this approach, we wouldn't.
There are plenty of cases today where it is just infeasible to translate all the way down, and we tend to stick to a syntactic translation only. This results in compiler errors in the resulting code quite often that need to be manually fixed, and that's okay. In this case, I personally think it's more important to preserve the values of the "enum" and its behavior than to eschew them in favor of making switch work, since switch can be replaced with if in this case.
However, your question got me thinking about an alternative approach. We could translate enums with values to be an abstract class with concrete implementations for each value. Example:
We don't do any kind of symbol resolution to know that this would need pattern matching, so either approach would have to be done manually by the user... but it's certainly an option.
I'm thinking we can have three configurable approaches for how to treat enums with values:
Discard values (current state) - possibly could add methods as extension methods on the enum type, but it still would lose values, which likely defeats the point. However some people might want to do it this way to keep using switch.
Sealed class with public static readonly values - loses ability to do switch without pattern matching on a property value, but (IMHO) cleaner code
Abstract class with implementation for each enumeration value - can switch with pattern matching on the type
And then it's up to the user of JavaToCSharp to decide how they want to translate it, until C# adds proper discriminated unions. If anyone has any other ideas without involving symbol resolution, let me know!
Fourth option, for those running C#9... we could take the approach from the link I posted about discriminated unions, and make them records inside an abstract class:
Hi, just found this project (got tons of old Java utils for small projects and was looking to convert one that happened to use this).
Since all Enum's extend java.lang.Enum that has an ordinal() final method, shouldn't that be considered the route to handling the switches if one wants to re-do translations often (or are the conversions more looking at idiomatic C# rather than good/easy conversion?), a shim of that one could of course.
As for abstract classes you kinda would need support translating like that anyhow, the code i was looking to translate looks like this with enums implementing abstract methods.
enum BinaryOperator {
PLUS("+",false) {
Object apply(Object l,Object r) {
if (l instanceof Double || r instanceof Double) {
return ((Number)l).doubleValue()+((Number)r).doubleValue();
} else if (l instanceof Long || r instanceof Long) {
return ((Number)l).longValue()+((Number)r).longValue();
} else if (l instanceof Integer || r instanceof Integer) {
return ((Number)l).intValue()+((Number)r).intValue();
}
throw new RuntimeException("+ not defined for "+l+" and "+r);
}
},
MINUS("-",false) {
Object apply(Object l,Object r) {
if (l instanceof Double || r instanceof Double) {
return ((Number)l).doubleValue()-((Number)r).doubleValue();
} else if (l instanceof Long || r instanceof Long) {
return ((Number)l).longValue()-((Number)r).longValue();
} else if (l instanceof Integer || r instanceof Integer) {
return ((Number)l).intValue()-((Number)r).intValue();
}
throw new RuntimeException("- not defined for "+l+" and "+r+" "+l.getClass());
}
}
;
String op;
boolean allowNull;
BinaryOperator(String op,boolean an) {
this.op=op;
this.allowNull=an;
}
abstract Object apply(Object l,Object r);
}
@whizzter Thanks for the real-world example. I had never (or rarely) encountered abstract methods in enums but you're right, that strengthens the case for the abstract class approach.
Well to be honest I'm not sure how much this is used in other places, It's was kinda specific but quite smooth solution to binding stuff for this interpreter.
I guess this is the kind of places where you need to consider if Java compatibility or C# target code elegantness/idiomaticness is more important. (Nr 1 is more important if you want to continually translate some cross-language project , Nr 2 if you want to migrate).
Might even be that having 2 variants depending on the source can be the right choice? Abstract being enforced for compatible conversions where required, but a leaner method chosen for non-tricky situations or explicitly for a simpler idiomatic migration-mode.
Activity
uxmal commentedon Jun 30, 2021
How are you going to generate switch statements using this construct?
paulirwin commentedon Jun 30, 2021
@uxmal Short answer - with this approach, we wouldn't.
There are plenty of cases today where it is just infeasible to translate all the way down, and we tend to stick to a syntactic translation only. This results in compiler errors in the resulting code quite often that need to be manually fixed, and that's okay. In this case, I personally think it's more important to preserve the values of the "enum" and its behavior than to eschew them in favor of making
switchwork, sinceswitchcan be replaced withifin this case.However, your question got me thinking about an alternative approach. We could translate enums with values to be an abstract class with concrete implementations for each value. Example:
Then, switch could be altered to use pattern matching on the type:
Note that with the sealed class approach, you could do pattern matching on the value, if that works for your use case:
We don't do any kind of symbol resolution to know that this would need pattern matching, so either approach would have to be done manually by the user... but it's certainly an option.
I'm thinking we can have three configurable approaches for how to treat enums with values:
public static readonlyvalues - loses ability to do switch without pattern matching on a property value, but (IMHO) cleaner codeAnd then it's up to the user of JavaToCSharp to decide how they want to translate it, until C# adds proper discriminated unions. If anyone has any other ideas without involving symbol resolution, let me know!
paulirwin commentedon Jun 30, 2021
Also my last example clearly could be rewritten as a standard enum with an int value... so pretend there's other fields there 😄
paulirwin commentedon Jun 30, 2021
Fourth option, for those running C#9... we could take the approach from the link I posted about discriminated unions, and make them records inside an abstract class:
paulirwin commentedon Jun 30, 2021
... actually I'm not sure if that has the same semantics. Still thinking about this...
whizzter commentedon Sep 21, 2021
Hi, just found this project (got tons of old Java utils for small projects and was looking to convert one that happened to use this).
Since all Enum's extend java.lang.Enum that has an ordinal() final method, shouldn't that be considered the route to handling the switches if one wants to re-do translations often (or are the conversions more looking at idiomatic C# rather than good/easy conversion?), a shim of that one could of course.
As for abstract classes you kinda would need support translating like that anyhow, the code i was looking to translate looks like this with enums implementing abstract methods.
paulirwin commentedon Sep 22, 2021
@whizzter Thanks for the real-world example. I had never (or rarely) encountered abstract methods in enums but you're right, that strengthens the case for the abstract class approach.
whizzter commentedon Sep 22, 2021
Well to be honest I'm not sure how much this is used in other places, It's was kinda specific but quite smooth solution to binding stuff for this interpreter.
I guess this is the kind of places where you need to consider if Java compatibility or C# target code elegantness/idiomaticness is more important. (Nr 1 is more important if you want to continually translate some cross-language project , Nr 2 if you want to migrate).
Might even be that having 2 variants depending on the source can be the right choice? Abstract being enforced for compatible conversions where required, but a leaner method chosen for non-tricky situations or explicitly for a simpler idiomatic migration-mode.
maximilien-noal commentedon May 22, 2024
This enum was converted to a C# enum with bit shifting and extension methods:
The original Java code:
For enums with more complicated private fields, I had to convert them into abstract classes:
The original Java code:
Just my 2 cents.