Follow-up of Is my usage of explicit casting operator reasonable or a bad hack?

One of the users commenting on that question said that it's really bad if the casting operator creates a new object under the hood, as it ceases to be an actual cast. It is of course very valid point.

When one would use the ability of defining custom type conversion? Especially: between two types that are not related by inheritance in any way.

With related objects it makes (some) sense:

class Animal {
    public string Name {get;set;}
    public int Age {get;set;}
}

class Dog : Animal {
    public void Bark(){}
    public void Fetch(){}
}

class Cat : Animal {
    public void Meow(){}
    public void Sleep(){}
}

Although normally casting Cat to Dog wouldn't be possible, it makes sense to explicitly allow it - they share all their data, just the behaviour is different.

Even when there would be some difference in fields:

class Parrot : Animal {
    public int WingSpan {get;set;}

    public void Screech(){}
}

casting still might be desired (Parrot to Cat for example, as Cat doesn't have MORE data than Parrot).

But what about two types that are not related? It is possible to do:

class Rock{
    public int Age {get;set;}
}

and

public static explicit operator Rock(Cat cat){
    return new Rock{Age = cat.Age};
}

but why? As pointed out in related question it would cause quite strange behaviour:

var cat = new Cat{Name = "Fluffy", Age = 8};
var rock = (Rock) cat;
rock.Age = 100000000;
Console.WriteLine(rock.Age); // 100000000
Console.WriteLine(cat.Age); // 8

Can anyone give a good example of when implementing explicit operator between unrelated types makes sense and how to do it?

有帮助吗?

解决方案

Can anyone give a good example of when implementing explicit operator between unrelated types makes sense and how to do it?

I will argue that it never makes sense to overload the explicit conversion operator. That operator has a well defined behavior, and using it for a different behavior will always fall into the realm of "clever". Just use a function.

That said, I've used the implicit conversion operator a few times, and I think that it can be used in a net-positive way.

For example, I have a small programming language I'm working on. It has an object to represent an Identifier token in the syntax tree, which is just a wrapper for a string to give it a concrete type (like this recent question asked about). So instead of writing new Identifier("blah") or if(identifer == new Identifier("class")) everywhere, I have an implicit conversion from string to Identifier. It simplifies the code while keeping the behavior clear.

Yes, it does create a new object during conversion. Yes, it requires me to make sure that Identifier has value-style equality and be sure that my class design won't cause horrible problems. But it's is one of those fairly dangerous tools you can use in a few situations to make things a lot more elegant if you know what you're doing.

Explicit conversion overrides have no such upside. They add verbosity to code anyways, so you're always better off using more conventional approaches.

其他提示

The casting operator is an operator, and like all operators you shouldn't overload it unless that overloading preserves the accepted meaning of the operator in the environment you work in. One of the reasons Scala confuses so many people is that it's libraries tend to hijack operators for DSL creation, making it very hard to understand the library and write code that uses it just from reading the API. Don't do that.

The same operators might have different accepted meanings in different languages - e.g. << and >> usually mean shift, but in C++ they also mean send-to/read-from stream. The accepted meaning can also come from the domain - for example many libraries that deal with paths like to use the division operator / for joining paths - because that's what used in the shell.

But the cast operator has the same general meaning everywhere - when I cast type X to type Y, I'm saying "a Y is essentially a X, only ...". The part after the only can make a world of difference - but I'd argue that any overload of cast that can't be translated to this template is an abuse of the case operator.

  • (string) 10 - a string is essentially a integer, only represented as text.
  • (int) 5.4 - an int is essentially a float, only without the fraction
  • (string) ['a', 'b', 'c'] - a string is essentially a collection of characters, only concatenated to each other.
  • (Rock) cat - a cat is essentially a rock, only... well...

It doesn't work in the last example - because a cat is not a rock, not matter how you put it. You are getting a weird behavior because what you are trying to do is weird. Just because both rock and cat have an age doesn't mean that a cat is essentially a rock with the same age - that kind of thinking is worse than Weak Typing - not only are you breaking the abstraction that strong typing provides to you that says that 1069547520 is different than 1.5 even though they are have the same representation in the memory - you are creating brand new conversions that make no sense neither in the high abstraction level nor in the low memory level.

So, to your question("Can anyone give a good example...") - the answer is no. Noone can give a good example, because there aren't any good example. Casting doesn't make sense when X and Y are unrelated, because the only cases where a cast operator makes sense is then you can say "a X is essentially a Y, only ...", and if you can say this - that's the relation between X and Y.

许可以下: CC-BY-SA归因
scroll top