Function style casts

Topics: C# Language Design
Jul 19, 2014 at 11:36 PM
Edited Jul 19, 2014 at 11:37 PM
I did a quick search and didn't see any previous requests for this, so if there was already I apologize. I'd like to see function style casts implemented in C#.

There are some times when it becomes hard to tell what is being casted, and function style casts help alleviate the problem:
short a = (short)(((byte)(b + c) + (int)(d + some_long_variable)) * 3);
Would look like this:
short a = short((byte(b + c) + int(d + some_long_variable)) * 3);
It also looks better when casting to function calls:
FileInfo file = new FileInfo("C:/tree.bmp");
File.ReadAllBytes((int)file.Length);
FileInfo file = new FileInfo("C:/tree.bmp");
File.ReadAllBytes(int(file.Length));
Overall, I think function style casts are better in every way than the current casts, and I know a lot of us would like this feature implemented. :) An added bonus would be allowing the new cast syntax on user defined types, and not just keywords (int, string, object, etc).
Jul 20, 2014 at 3:16 AM
Looks clean.
Jul 20, 2014 at 2:53 PM
I agree that it is in fact more syntactically concise, but it's also ambiguous. Consider:
class Test {}

class Program
{
    int Test(object o) { return 0; }
    void T()
    {
        object o = new Test();
        var x = Test(o); // Is that a cast to Test or the invocation of the Test method?
    }
}
Jul 20, 2014 at 3:06 PM
Edited Jul 20, 2014 at 3:16 PM
This is precisely why my original post mentioned that user-defined type casts would be an added bonus. :)

I would assume that the compiler wouldn't allow that because it's ambiguous. There's already precedent for this:
class Test {
    public string someMethod(int value) {
        return "";
    }
    public int someMethod(int value) {
        return 0;
    }
}

class Program {
    static void Main() {
        var o = new Test();
        var x = o.someMethod(0); // <-- ambiguous call, doesn't compile.
    }
}
Same thing occurs for ambiguous type names, ambiguous variable names, etc. So why not enforce the same rules for function style casts? If it's really necessary, the compiler could ignore this condition when targeting previous frameworks so backwards compatibility is maintained.

Also, speaking of ambiguity:
var some_long_var = -1L;
var text = (int)(some_long_var).ToString("x");
// is text 'ffffffff' or 'ffffffffffffffff'?
// this also looks like you're casting ToString("x") to an integer.
These kinds of situations are much less ambiguous:
var some_long_var = -1L;
var text = int(some_long_var).ToString("x");
// text is definitely 'ffffffff'
You could make the former example less ambiguous by adding even more parenthesis:
var some_long_var = -1L;
var text = ((int)(some_long_var)).ToString("x");
// text is definitely 'ffffffff'
But now the code looks like a train wreck, and those parenthesis are very fragile and difficult to see which ones belong together.
Jul 20, 2014 at 3:19 PM
Edited Jul 20, 2014 at 3:26 PM
Ah, sorry, I missed that part. Anyway, your example is somewhat different: You get the error as soon as you declare the second method, which only differs in its return type from the first one. In my example above, adding a method Test to a class might now start breaking code (within that class) that previously compiled just fine. Or, even worse, adding a class called Test might now break previously fine code all throughout your code base. Or even adding a using declaration (of a namespace containing a type called Test) might have this effect. So I see no way to make the new syntax work for user-defined types.

Anyway, I'm not in favor of adding a new cast operator just for primitive types. That seems too limited and it is probably going to surprise developers once they find out that they can't use the new syntax for user-defined types.

Edit: One could go the route that C++ took and add a special cast operator to the language similar to static_cast<T> or dynamic_cast<T>. That would solve the ambiguity, but gives up conciseness.
var some_long_var = -1L;
var text = static_cast<int>(some_long_var).ToString("x");
Jul 20, 2014 at 3:51 PM
Edited Jul 20, 2014 at 3:54 PM
Good point. One could make the compiler prefer methods over types if such a name collision is found. There's always going to be situations where ambiguity can exist, but I don't think that's reason enough to not consider a feature, 99% of the time the cast syntax would be just fine. And for those cases where it isn't, the compiler would default to a local function call.

In the event where that still isn't enough (you have a local function call, but you want the cast instead), one could use namespace qualifiers:
namespace MyNameSpace {
    class Test {
        public int Integer { get; set; }
    }
}

// shorthand namespaces:
void Test(object value) {
    MyNameSpace.Test(value).Integer++; // this is starting to get ugly
}
// absolute namespaces:
void Test(object value) {
    global::MyNameSpace.Test(value).Integer++; // at this point, a ((Test)value) cast would probably be preferable.
}
And if that still isn't enough, then perhaps your code base could use a little tidying up :) But let's not be confused, these cases are the exception, not the rule. How often are you going to have a method called 'BinaryReader' or 'StringBuilder' or 'CultureInfo' in your code, who's method signature matches exactly what those types have implicit/explicit conversion operators for? Very rarely I would think.

Also, I'm not a fan of the static_cast/dynamic_cast for the same reason the above code is commented with "this is starting to get ugly". For the many cases where function style casts would be just fine, it's a lot cleaner to read.
Jul 28, 2014 at 9:24 PM
I don't really like this suggestion. As of now, we have clear distinction: (T)value is a cast, F(value) is a function call. With the proposal in question, we are bringing in a duplicate facility (another syntax for cast), which clashes with the already existing one. So it doesn't contribute to the expressiveness of the language, and on the contrary makes it less readable and understandable.

(And the proposed syntax would look too much like C++ which is an additional disadvantage.)
Jul 29, 2014 at 8:22 AM
One annoying thing of casts and (negation / bit inversion for the matter) is that the operand is at the begging. As expressions are more List-like and not Tree-like this looks strange and forces you to move back:
(DogDN)Me.Mother.Mother.Child[0].Pet;
will look better as
Me.Mother.Mother.Child[0].Pet cast DogDN;
'cast' keyword could be the third brother of 'is' and 'as'. If you need member access you'll need moving back for parenthesis. :S
Jul 30, 2014 at 5:17 PM
VladD wrote:
I don't really like this suggestion. As of now, we have clear distinction: (T)value is a cast, F(value) is a function call. With the proposal in question, we are bringing in a duplicate facility (another syntax for cast), which clashes with the already existing one. So it doesn't contribute to the expressiveness of the language, and on the contrary makes it less readable and understandable.
C# allows casting syntax to be used in four distinct scenarios:
  1. The original and destination types are both the same class
  2. The original is a subtype of the destination
  3. The original is a supertype of the destination.
  4. The compiler can determine that none of the above apply.
The first three scenarios are representatation-preserving, but the fourth is semantically equivalent to a function call (note that casting a class-type expression to its own type is a no-op, but casting a value-type expression to its own type isn't always). Since the creators of C# has decided to use C's syntax for all four styles, there's no way now to require different syntax for the different usages, but conceptually it might have been nice to have the thing that behaves like a function call be written like one.