Question

Isn't it more logical to have animal(Elephant).UseTrunk() ? This way you don't have to clutter your code with all these extra parantheses like in ((Elephant)animal).UseTrunk()

Is this for historic reasons? would it interfere with other syntax? or am I missing something else?

I have an almost religious respect for the clean and pureness of the C# syntax and language, but the casting syntax always feels a bit like the black sheep to me.

PS. I do know about (animal as Elephant).UseTrunk(), but that's not the question :) besides that it is in theory a workaround - breaking the idea of the clean and pureness of the language.

Was it helpful?

Solution 4

the syntax you suggest is ambiguous! Since dot is a primary operator in c#, it has the highest precedence, so without using parenthesis how do you parse the animal(Elephant).UseTrunk()? As you said, UseTrunk() should be applied for animal(Elephant) rather than (Elephant) and since dot has the higher precedence it will produce some sort of ambiguity. But if you wanna lower the precedence of dot then as a language designer, you'll face much more complications. Besides, with that kind of syntax, how would you handle method calls? How would you distinguish method call from cast operator? So practically the best way is to use extra pairs of parenthesis to control the situation.

OTHER TIPS

((Elephant)animal).UseTrunk() comes from C++ from which C# is derived.

C++ also has functional syntax Elephant(animal).UseTrunk(), but C# language designers decided to not support it. The reason is that it provides very few benefits while complicating logic for name resolving. Well-designed programs shouldn't use type casting a lot, so this isn't a problem.

You can simplify your code by using the following extension method:

    public static T To<T> (this object @this)
    {
        return (T)@this;
    }

Your example becomes animal.To<Elephant>().UseTrunk() which is pretty close to the syntax you propose.

The syntax

((Elephant) animal).UseTrunk();

comes from C, C++. The new C# possibilities are is and as. If Elephant is a class, not struct the most accurate cast, IMHO, is

  Elephant elephant = animal as Elephant;

  if (!Object.ReferenceEquals(null, elephant))
    elephant.UseTrunk();
  else {
    // animal isn't an elephant - do nothing, throw exception etc.
  }

But often (if you're quite sure that animal is fact an Elephant) you can simply put

  (animal as Elephant).UseTrunk(); // <- Not recommended, see Athari's comment

If Elephant is a struct, in the most accurate case you have to do something like

  // Drawback: you have to cast twice
  if (animal is Elephant) // <- 1st cast
    ((Elephant) animal).UseTrunk(); // <- 2nd cast

As others have noted, this prefix cast syntax goes back at least to the C language, predating object oriented programming by quite some time.

If it was a postfix operator, as in your example, it would be harder to for the parser to distinguish it from function calls. Prefix parentheses weren't already in use.

It really does boil down to "we need a syntax, this one is easy to implement and not painfully ugly, go for it."

There is (animal as Elephant).UseTrunk(), same ordering and same number of parentheses. But I still prefer the regular casting. It comes from C or even earlier, so yes, historical reasons.

I do find what you propose a little more concise, it can save a few keystrokes. But it can be confusing when it comes to methods. Take a few comparisons of worst cases:

var i = (int)Get(); //existing syntax
var i = Get()(int); //what you propose

var i = (int)Get<int>(typeof(int)); //existing syntax
var i = Get<int>(typeof(int))(int); //what you propose

var i = ((Mammoth)(Elephant)Get()).Trunk; //existing syntax
var i = Get()(Elephant)(Mammoth).Trunk; //what you propose

var i = ((Mammoth)((Elephant)Get()).Trunk).Trunk; //existing syntax
var i = Get()(Elephant).Trunk(Mammoth).Trunk; //what you propose

To each his own in the end. Languages like python has a more forward style approach, for instance:

i = int(animal);

but that's not technically cast operator.

Isn't it more logical to have animal(Elephant).UseTrunk().

That syntax is already used by C# (and C, from whence it came). It means, "Call the function animal, passing in Elephant as an argument. Then call UseTrunk() on the result of that."

If you also allowed that syntax as a type coercion, you'd have to figure out some way to disambiguate the two. One way to tweak the syntax would be like so:

animal.(Elephant).UseTrack()

The .( makes it unambiguous since that syntax is not currently being used for anything. And, indeed, this is exactly the syntax choice Go made for type coercions.

Because not every animal have useTrunk() but it should be a special method for an elephant. Let's say every animal has walk(), so you can just use animal().walk(), but you can't call animal().useTrunk() unless you can confirm that this class can be cast (this animal absolutely) to an elephant. I think this is the basic idea of Object Oriented.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top